vortex_fastlanes/delta/compute/
cast.rs1use vortex_array::ArrayRef;
5use vortex_array::ArrayView;
6use vortex_array::IntoArray;
7use vortex_array::builtins::ArrayBuiltins;
8use vortex_array::dtype::DType;
9use vortex_array::dtype::Nullability::NonNullable;
10use vortex_array::scalar_fn::fns::cast::CastReduce;
11use vortex_error::VortexResult;
12
13use crate::delta::Delta;
14use crate::delta::array::DeltaArrayExt;
15use crate::delta::array::DeltaArraySlotsExt;
16
17impl CastReduce for Delta {
18 fn cast(array: ArrayView<'_, Self>, dtype: &DType) -> VortexResult<Option<ArrayRef>> {
19 let DType::Primitive(target_ptype, target_nullability) = dtype else {
20 return Ok(None);
21 };
22
23 if array.dtype().as_ptype() != *target_ptype {
26 return Ok(None);
27 }
28 if *target_nullability == NonNullable && array.dtype().nullability() != NonNullable {
29 return Ok(None);
30 }
31
32 let casted_bases = array.bases().cast(dtype.with_nullability(NonNullable))?;
33 let casted_deltas = array.deltas().cast(dtype.clone())?;
34
35 Ok(Some(
36 Delta::try_new(casted_bases, casted_deltas, array.offset(), array.len())?.into_array(),
37 ))
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use std::sync::LazyLock;
44
45 use rstest::rstest;
46 use vortex_array::IntoArray;
47 use vortex_array::VortexSessionExecute;
48 use vortex_array::arrays::PrimitiveArray;
49 use vortex_array::assert_arrays_eq;
50 use vortex_array::builtins::ArrayBuiltins;
51 use vortex_array::compute::conformance::cast::test_cast_conformance;
52 use vortex_array::dtype::DType;
53 use vortex_array::dtype::Nullability;
54 use vortex_array::dtype::PType;
55 use vortex_buffer::buffer;
56 use vortex_error::VortexResult;
57 use vortex_session::VortexSession;
58
59 use crate::Delta;
60 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
61 let session = vortex_array::array_session();
62 crate::initialize(&session);
63 session
64 });
65
66 #[test]
67 fn test_cast_delta_unsigned_widening_wraps() {
68 let primitive = PrimitiveArray::from_iter([200u8, 50, 75, 10, 255]);
74 let array =
75 Delta::try_from_primitive_array(&primitive, &mut SESSION.create_execution_ctx())
76 .unwrap();
77
78 let casted = array
79 .into_array()
80 .cast(DType::Primitive(PType::U32, Nullability::NonNullable))
81 .unwrap();
82
83 assert_arrays_eq!(
84 casted,
85 PrimitiveArray::from_iter([200u32, 50, 75, 10, 255]),
86 &mut SESSION.create_execution_ctx()
87 );
88 }
89
90 #[test]
91 fn test_cast_delta_same_width_float_target() {
92 let primitive = PrimitiveArray::from_iter([10u32, 20, 30, 40, 50]);
96 let array =
97 Delta::try_from_primitive_array(&primitive, &mut SESSION.create_execution_ctx())
98 .unwrap();
99
100 let casted = array
101 .into_array()
102 .cast(DType::Primitive(PType::F32, Nullability::NonNullable))
103 .unwrap();
104
105 assert_arrays_eq!(
106 casted,
107 PrimitiveArray::from_iter([10f32, 20.0, 30.0, 40.0, 50.0]),
108 &mut SESSION.create_execution_ctx()
109 );
110 }
111
112 #[test]
113 fn test_cast_delta_add_nullability() -> VortexResult<()> {
114 let values = PrimitiveArray::from_iter([10u32, 20, 5, 30, 15]);
116 let array = Delta::try_from_primitive_array(&values, &mut SESSION.create_execution_ctx())?;
117
118 let casted = array
119 .into_array()
120 .cast(DType::Primitive(PType::U32, Nullability::Nullable))?;
121
122 assert_eq!(
123 casted.dtype(),
124 &DType::Primitive(PType::U32, Nullability::Nullable)
125 );
126 assert_arrays_eq!(
127 casted,
128 PrimitiveArray::from_option_iter([Some(10u32), Some(20), Some(5), Some(30), Some(15)]),
129 &mut SESSION.create_execution_ctx()
130 );
131 Ok(())
132 }
133
134 #[test]
135 fn test_cast_delta_nullability_preserves_nulls() -> VortexResult<()> {
136 let values =
139 PrimitiveArray::from_option_iter([Some(10u32), None, Some(30), Some(15), None]);
140 let array = Delta::try_from_primitive_array(&values, &mut SESSION.create_execution_ctx())?;
141
142 let casted = array
143 .into_array()
144 .cast(DType::Primitive(PType::U32, Nullability::Nullable))?;
145
146 assert_arrays_eq!(
147 casted,
148 PrimitiveArray::from_option_iter([Some(10u32), None, Some(30), Some(15), None]),
149 &mut SESSION.create_execution_ctx()
150 );
151 Ok(())
152 }
153
154 #[test]
155 fn test_cast_delta_drop_nullability_with_nulls_errors() -> VortexResult<()> {
156 let values = PrimitiveArray::from_option_iter([Some(10u32), None, Some(30)]);
159 let array = Delta::try_from_primitive_array(&values, &mut SESSION.create_execution_ctx())?;
160
161 #[expect(deprecated)]
163 let result = array
164 .into_array()
165 .cast(DType::Primitive(PType::U32, Nullability::NonNullable))
166 .and_then(|a| a.to_canonical().map(|c| c.into_array()));
167
168 assert!(
169 result.is_err(),
170 "dropping nullability with real nulls must error, got {result:?}"
171 );
172 Ok(())
173 }
174
175 #[test]
176 fn test_cast_delta_u8_to_u32() {
177 let primitive = PrimitiveArray::from_iter([10u8, 20, 30, 40, 50]);
178 let array =
179 Delta::try_from_primitive_array(&primitive, &mut SESSION.create_execution_ctx())
180 .unwrap();
181
182 let casted = array
183 .into_array()
184 .cast(DType::Primitive(PType::U32, Nullability::NonNullable))
185 .unwrap();
186 assert_eq!(
187 casted.dtype(),
188 &DType::Primitive(PType::U32, Nullability::NonNullable)
189 );
190
191 assert_arrays_eq!(
193 casted,
194 PrimitiveArray::from_iter([10u32, 20, 30, 40, 50]),
195 &mut SESSION.create_execution_ctx()
196 );
197 }
198
199 #[test]
200 fn test_cast_delta_nullable() {
201 let values = PrimitiveArray::new(
203 buffer![100u16, 0, 200, 300, 0],
204 vortex_array::validity::Validity::NonNullable,
205 );
206 let array =
207 Delta::try_from_primitive_array(&values, &mut SESSION.create_execution_ctx()).unwrap();
208
209 let casted = array
210 .into_array()
211 .cast(DType::Primitive(PType::U32, Nullability::Nullable))
212 .unwrap();
213 assert_eq!(
214 casted.dtype(),
215 &DType::Primitive(PType::U32, Nullability::Nullable)
216 );
217 }
218
219 #[rstest]
220 #[case::u8(
221 PrimitiveArray::new(
222 buffer![0u8, 10, 20, 30, 40, 50],
223 vortex_array::validity::Validity::NonNullable,
224 )
225 )]
226 #[case::u16(
227 PrimitiveArray::new(
228 buffer![0u16, 100, 200, 300, 400, 500],
229 vortex_array::validity::Validity::NonNullable,
230 )
231 )]
232 #[case::u32(
233 PrimitiveArray::new(
234 buffer![0u32, 1000, 2000, 3000, 4000],
235 vortex_array::validity::Validity::NonNullable,
236 )
237 )]
238 #[case::u64(
239 PrimitiveArray::new(
240 buffer![0u64, 10000, 20000, 30000],
241 vortex_array::validity::Validity::NonNullable,
242 )
243 )]
244 fn test_cast_delta_conformance(#[case] primitive: PrimitiveArray) {
245 let delta_array =
246 Delta::try_from_primitive_array(&primitive, &mut SESSION.create_execution_ctx())
247 .unwrap();
248 test_cast_conformance(
249 &delta_array.into_array(),
250 &mut SESSION.create_execution_ctx(),
251 );
252 }
253}