vortex_array/array/probe/
repeated.rs1use std::any::Any;
5
6use vortex_error::VortexResult;
7use vortex_error::vortex_err;
8
9use crate::ArrayRef;
10use crate::ExecutionCtx;
11use crate::IntoArray;
12use crate::array::probe::ArrayProbe;
13use crate::array::probe::array::check_bounds;
14use crate::array::probe::array::check_dtype;
15use crate::array::probe::array::child_of;
16use crate::arrays::BoolArray;
17use crate::arrays::ScalarFn;
18use crate::scalar::Scalar;
19use crate::validity::Validity;
20
21pub struct RepeatedArrayProbe {
28 array: ArrayRef,
29 state: Option<Box<dyn Any>>,
31}
32
33impl RepeatedArrayProbe {
34 pub fn new(array: ArrayRef) -> Self {
36 Self { array, state: None }
37 }
38
39 pub fn array(&self) -> &ArrayRef {
41 &self.array
42 }
43
44 #[inline]
46 pub fn as_probe(&mut self) -> ArrayProbe<'_> {
47 ArrayProbe::Repeated(self)
48 }
49
50 pub fn execute_scalar(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<Scalar> {
52 check_bounds(&self.array, index)?;
53 let result =
54 self.array
55 .dyn_array()
56 .probe_scalar_retained(&self.array, index, &mut self.state, ctx);
57 check_dtype(&self.array, result)
58 }
59
60 pub fn execute_is_valid(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
62 check_bounds(&self.array, index)?;
63 if !self.array.dtype().is_nullable() {
64 return Ok(true);
65 }
66 self.array
67 .dyn_array()
68 .probe_is_valid_retained(&self.array, index, &mut self.state, ctx)
69 }
70
71 pub fn execute_is_invalid(
73 &mut self,
74 index: usize,
75 ctx: &mut ExecutionCtx,
76 ) -> VortexResult<bool> {
77 Ok(!self.execute_is_valid(index, ctx)?)
78 }
79}
80
81enum ResolvedValidity {
83 Uniform(bool),
84 Array(RepeatedArrayProbe),
85}
86
87impl ResolvedValidity {
88 #[inline]
90 fn is_valid(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
91 match self {
92 Self::Uniform(valid) => Ok(*valid),
93 Self::Array(probe) => is_valid_scalar(probe.execute_scalar(index, ctx)?, index),
94 }
95 }
96
97 fn resolve(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self> {
99 if !array.dtype().is_nullable() {
100 return Ok(Self::Uniform(true));
101 }
102 Ok(match array.validity()? {
103 Validity::NonNullable | Validity::AllValid => Self::Uniform(true),
104 Validity::AllInvalid => Self::Uniform(false),
105 Validity::Array(validity) => {
106 let validity = if array.is::<ScalarFn>() {
109 validity.execute::<BoolArray>(ctx)?.into_array()
110 } else {
111 validity
112 };
113 Self::Array(RepeatedArrayProbe::new(validity))
114 }
115 })
116 }
117}
118
119#[inline]
121pub(super) fn is_valid_scalar(scalar: Scalar, index: usize) -> VortexResult<bool> {
122 scalar
123 .as_bool()
124 .value()
125 .ok_or_else(|| vortex_err!("validity value at index {index} is null"))
126}
127
128pub(crate) fn repeated_state<S: Default + 'static>(
130 slot: &mut Option<Box<dyn Any>>,
131) -> VortexResult<&mut RepeatedState<S>> {
132 slot.get_or_insert_with(|| Box::new(RepeatedState::<S>::default()))
133 .downcast_mut::<RepeatedState<S>>()
134 .ok_or_else(|| vortex_err!("Probe state type mismatch"))
135}
136
137pub(crate) struct RepeatedState<S> {
139 state: S,
140 slots: Vec<Option<RepeatedArrayProbe>>,
143 validity: Option<ResolvedValidity>,
145}
146
147impl<S: Default> Default for RepeatedState<S> {
148 fn default() -> Self {
149 Self {
150 state: S::default(),
151 slots: Vec::new(),
152 validity: None,
153 }
154 }
155}
156
157impl<S> RepeatedState<S> {
158 #[inline]
159 pub(super) fn state_mut(&mut self) -> &mut S {
160 &mut self.state
161 }
162
163 #[inline]
165 pub(super) fn split_mut(&mut self) -> (&mut S, &mut Vec<Option<RepeatedArrayProbe>>) {
166 (&mut self.state, &mut self.slots)
167 }
168
169 #[inline]
171 pub(crate) fn is_valid(
172 &mut self,
173 array: &ArrayRef,
174 index: usize,
175 ctx: &mut ExecutionCtx,
176 ) -> VortexResult<bool> {
177 let validity = match &mut self.validity {
178 Some(validity) => validity,
179 slot @ None => slot.insert(ResolvedValidity::resolve(array, ctx)?),
180 };
181 validity.is_valid(index, ctx)
182 }
183}
184
185pub(super) fn child_probe<'s>(
187 slots: &'s mut Vec<Option<RepeatedArrayProbe>>,
188 parent: &ArrayRef,
189 slot: usize,
190) -> VortexResult<Option<&'s mut RepeatedArrayProbe>> {
191 let Some(child) = child_of(parent, slot)? else {
192 return Ok(None);
193 };
194 if slots.is_empty() {
195 slots.resize_with(parent.slots().len(), || None);
196 }
197 Ok(Some(slots[slot].get_or_insert_with(|| {
198 RepeatedArrayProbe::new(child.clone())
199 })))
200}
201
202#[cfg(test)]
203mod tests {
204 use vortex_error::VortexResult;
205 use vortex_error::vortex_err;
206
207 use super::*;
208 use crate::VortexSessionExecute;
209 use crate::array::IntoArray;
210 use crate::array::probe::ProbeState;
211 use crate::arrays::PrimitiveArray;
212 use crate::arrays::Struct;
213 use crate::arrays::StructArray;
214
215 fn nullable_ints() -> ArrayRef {
216 PrimitiveArray::from_option_iter([Some(10i32), None, Some(30)]).into_array()
217 }
218
219 fn check_reads(mut probe: ArrayProbe<'_>, ctx: &mut ExecutionCtx) -> VortexResult<()> {
220 assert!(probe.execute_scalar(3, ctx).is_err());
221 assert!(probe.execute_is_valid(3, ctx).is_err());
222 assert!(probe.execute_scalar(1, ctx)?.is_null());
223 assert!(!probe.execute_is_valid(1, ctx)?);
224 assert!(probe.execute_is_invalid(1, ctx)?);
225 assert_eq!(probe.execute_scalar(2, ctx)?, Scalar::from(Some(30i32)));
226 assert_eq!(probe.execute_scalar(0, ctx)?, Scalar::from(Some(10i32)));
227 Ok(())
228 }
229
230 #[test]
231 fn repeated_probe_initializes_lazily_and_outlives_handle() -> VortexResult<()> {
232 let mut ctx = crate::array_session().create_execution_ctx();
233 let array = nullable_ints();
234 let mut probe = RepeatedArrayProbe::new(array.clone());
235 drop(array);
236 assert!(probe.state.is_none());
237
238 check_reads(probe.as_probe(), &mut ctx)?;
239
240 let state = repeated_state::<()>(&mut probe.state)?;
241 assert!(matches!(state.validity, Some(ResolvedValidity::Array(_))));
242 Ok(())
243 }
244
245 #[test]
246 fn repeated_probe_resolves_uniform_validity_once() -> VortexResult<()> {
247 let mut ctx = crate::array_session().create_execution_ctx();
248 let array = PrimitiveArray::from_option_iter([Some(1i32), Some(2)]).into_array();
249 let mut probe = array.repeated_probe();
250 assert!(probe.execute_is_valid(1, &mut ctx)?);
251 let state = repeated_state::<()>(&mut probe.state)?;
252 assert!(matches!(
253 state.validity,
254 Some(ResolvedValidity::Uniform(true))
255 ));
256 assert_eq!(probe.execute_scalar(1, &mut ctx)?, Scalar::from(Some(2i32)));
257 Ok(())
258 }
259
260 #[test]
261 fn validity_and_scalar_reads_share_the_validity_slot() -> VortexResult<()> {
262 let mut ctx = crate::array_session().create_execution_ctx();
263 let mut probe = nullable_ints().repeated_probe();
264 assert!(!probe.execute_is_valid(1, &mut ctx)?);
265 let Some(ResolvedValidity::Array(validity)) =
266 &repeated_state::<()>(&mut probe.state)?.validity
267 else {
268 return Err(vortex_err!("validity slot should hold a probe"));
269 };
270 assert!(validity.state.is_some());
271 assert!(probe.execute_scalar(1, &mut ctx)?.is_null());
272 assert_eq!(
273 probe.execute_scalar(2, &mut ctx)?,
274 Scalar::from(Some(30i32))
275 );
276 Ok(())
277 }
278
279 #[test]
280 fn repeated_probe_reads_null_rows_through_encoding() -> VortexResult<()> {
281 let mut ctx = crate::array_session().create_execution_ctx();
282 let array = PrimitiveArray::from_option_iter([Some(1i32), None]).into_array();
283 let mut probe = array.repeated_probe();
284 assert!(probe.execute_scalar(1, &mut ctx)?.is_null());
285 assert_eq!(probe.execute_scalar(0, &mut ctx)?, Scalar::from(Some(1i32)));
286 assert!(!probe.execute_is_valid(1, &mut ctx)?);
287 Ok(())
288 }
289
290 #[test]
291 fn repeated_state_creates_children_on_demand() -> VortexResult<()> {
292 let mut ctx = crate::array_session().create_execution_ctx();
293 let array = struct_of_two_fields()?;
294 let typed = array
295 .as_opt::<Struct>()
296 .ok_or_else(|| vortex_err!("expected a struct"))?;
297 let mut repeated = RepeatedState::<()>::default();
298 {
299 let mut state = ProbeState::repeated(typed, &mut repeated);
300 assert!(state.slot(5).is_err());
301 assert!(state.slot(0)?.is_none());
302 assert!(state.retained().is_some());
303 let mut field = state.slot(2)?.ok_or_else(|| vortex_err!("missing field"))?;
304 assert!(matches!(field, ArrayProbe::Repeated(_)));
305 assert_eq!(field.execute_scalar(1, &mut ctx)?, Scalar::from(4i64));
306 assert_eq!(field.execute_scalar(0, &mut ctx)?, Scalar::from(3i64));
307 }
308
309 assert_eq!(repeated.slots.len(), array.slots().len());
310 assert!(repeated.slots[1].is_none());
311 let child = repeated.slots[2]
312 .as_ref()
313 .ok_or_else(|| vortex_err!("missing child probe"))?;
314 assert_eq!(child.array().len(), 2);
315 Ok(())
316 }
317
318 fn struct_of_two_fields() -> VortexResult<ArrayRef> {
319 Ok(StructArray::from_fields(&[
320 ("a", PrimitiveArray::from_iter([1i32, 2]).into_array()),
321 ("b", PrimitiveArray::from_iter([3i64, 4]).into_array()),
322 ])?
323 .into_array())
324 }
325
326 #[test]
327 fn state_slot_rejects_mismatched_type() -> VortexResult<()> {
328 let mut slot: Option<Box<dyn Any>> = None;
329 repeated_state::<()>(&mut slot)?;
330 assert!(repeated_state::<u8>(&mut slot).is_err());
331 Ok(())
332 }
333}