1mod expr;
5
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::ops::BitAnd;
9use std::ops::Range;
10use std::sync::Arc;
11use std::sync::OnceLock;
12
13use Nullability::NonNullable;
14pub use expr::*;
15use futures::FutureExt;
16use futures::future::BoxFuture;
17use vortex_array::ArrayRef;
18use vortex_array::IntoArray;
19use vortex_array::MaskFuture;
20use vortex_array::VortexSessionExecute;
21use vortex_array::builtins::ArrayBuiltins;
22use vortex_array::dtype::DType;
23use vortex_array::dtype::FieldMask;
24use vortex_array::dtype::FieldName;
25use vortex_array::dtype::Nullability;
26use vortex_array::dtype::PType;
27use vortex_array::expr::BoundExpression;
28use vortex_array::expr::ExactBoundExpr;
29use vortex_array::expr::transform::BoundPartitionedExpr;
30use vortex_array::expr::transform::partition_bound;
31use vortex_array::expr::traversal::NodeExt;
32use vortex_array::expr::traversal::Transformed;
33use vortex_array::expr::traversal::TraversalOrder;
34use vortex_array::scalar::PValue;
35use vortex_error::VortexExpect;
36use vortex_error::VortexResult;
37use vortex_mask::Mask;
38use vortex_sequence::Sequence;
39use vortex_sequence::SequenceArray;
40use vortex_session::VortexSession;
41use vortex_utils::aliases::dash_map::DashMap;
42
43use crate::ArrayFuture;
44use crate::LayoutReader;
45use crate::RowSplits;
46use crate::SplitRange;
47use crate::layouts::partitioned::BoundPartitionedExprEval;
48
49pub struct RowIdxLayoutReader {
50 name: Arc<str>,
51 row_offset: u64,
52 child: Arc<dyn LayoutReader>,
53 partition_cache: DashMap<ExactBoundExpr, Arc<OnceLock<Partitioning>>>,
54 session: VortexSession,
55}
56
57impl RowIdxLayoutReader {
58 pub fn new(row_offset: u64, child: Arc<dyn LayoutReader>, session: VortexSession) -> Self {
59 Self {
60 name: Arc::clone(child.name()),
61 row_offset,
62 child,
63 partition_cache: DashMap::with_hasher_and_shard_amount(Default::default(), 8),
64 session,
65 }
66 }
67
68 fn partition_expr(&self, expr: &BoundExpression) -> VortexResult<Partitioning> {
69 let key = ExactBoundExpr(expr.clone());
70
71 if let Some(entry) = self.partition_cache.get(&key)
73 && let Some(partitioning) = entry.value().get()
74 {
75 return Ok(partitioning.clone());
76 }
77
78 let result = self.compute_partitioning(expr)?;
79
80 self.partition_cache
81 .entry(key)
82 .or_insert_with(|| Arc::new(OnceLock::new()))
83 .get_or_init(|| result.clone());
84
85 Ok(result)
86 }
87
88 fn compute_partitioning(&self, expr: &BoundExpression) -> VortexResult<Partitioning> {
89 let mut partitioned = partition_bound(expr.clone(), |expr: &BoundExpression| {
91 if expr
92 .as_scalar()
93 .is_some_and(|scalar_fn| scalar_fn.is::<RowIdx>())
94 {
95 vec![Partition::RowIdx]
96 } else if expr.is_root() {
97 vec![Partition::Child]
98 } else {
99 vec![]
100 }
101 })?;
102
103 if partitioned.partitions.len() == 1 {
105 return Ok(match &partitioned.partition_annotations[0] {
106 Partition::RowIdx => Partitioning::RowIdx(replace_row_idx(expr.clone())?),
107 Partition::Child => Partitioning::Child(expr.clone()),
108 });
109 }
110
111 let partitions = partitioned
113 .partitions
114 .iter()
115 .cloned()
116 .map(replace_row_idx)
117 .collect::<VortexResult<Vec<_>>>()?
118 .into_boxed_slice();
119 partitioned.replace_partitions(partitions)?;
120
121 Ok(Partitioning::Partitioned(Arc::new(partitioned)))
122 }
123}
124
125#[derive(Clone)]
126enum Partitioning {
127 RowIdx(BoundExpression),
129 Child(BoundExpression),
131 Partitioned(Arc<BoundPartitionedExpr<Partition>>),
133}
134
135#[derive(Clone, PartialEq, Eq, Hash)]
136enum Partition {
137 RowIdx,
138 Child,
139}
140
141impl Partition {
142 pub fn name(&self) -> &str {
143 match self {
144 Partition::RowIdx => "row_idx",
145 Partition::Child => "child",
146 }
147 }
148}
149
150impl From<Partition> for FieldName {
151 fn from(value: Partition) -> Self {
152 FieldName::from(value.name())
153 }
154}
155
156impl Display for Partition {
157 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
158 write!(f, "{}", self.name())
159 }
160}
161
162impl LayoutReader for RowIdxLayoutReader {
163 fn name(&self) -> &Arc<str> {
164 &self.name
165 }
166
167 fn dtype(&self) -> &DType {
168 self.child.dtype()
169 }
170
171 fn row_count(&self) -> u64 {
172 self.child.row_count()
173 }
174
175 fn register_splits(
176 &self,
177 field_mask: &[FieldMask],
178 split_range: &SplitRange,
179 splits: &mut RowSplits,
180 ) -> VortexResult<()> {
181 self.child.register_splits(field_mask, split_range, splits)
182 }
183
184 fn pruning_evaluation(
185 &self,
186 row_range: &Range<u64>,
187 expr: &BoundExpression,
188 mask: Mask,
189 ) -> VortexResult<MaskFuture> {
190 Ok(match &self.partition_expr(expr)? {
191 Partitioning::RowIdx(expr) => row_idx_mask_future(
192 self.row_offset,
193 row_range,
194 expr.clone(),
195 MaskFuture::ready(mask),
196 self.session.clone(),
197 ),
198 Partitioning::Child(expr) => self.child.pruning_evaluation(row_range, expr, mask)?,
199 Partitioning::Partitioned(..) => MaskFuture::ready(mask),
200 })
201 }
202
203 fn filter_evaluation(
204 &self,
205 row_range: &Range<u64>,
206 expr: &BoundExpression,
207 mask: MaskFuture,
208 ) -> VortexResult<MaskFuture> {
209 match &self.partition_expr(expr)? {
210 Partitioning::RowIdx(_) => Ok(mask),
213 Partitioning::Child(expr) => self.child.filter_evaluation(row_range, expr, mask),
214 Partitioning::Partitioned(p) => Arc::clone(p).into_mask_future(
215 mask,
216 |annotation, expr, mask| match annotation {
217 Partition::RowIdx => Ok(row_idx_mask_future(
218 self.row_offset,
219 row_range,
220 expr.clone(),
221 mask,
222 self.session.clone(),
223 )),
224 Partition::Child => self.child.filter_evaluation(row_range, expr, mask),
225 },
226 |annotation, expr, mask| match annotation {
227 Partition::RowIdx => Ok(row_idx_array_future(
228 self.row_offset,
229 row_range,
230 expr.clone(),
231 mask,
232 )),
233 Partition::Child => self.child.projection_evaluation(row_range, expr, mask),
234 },
235 self.session.clone(),
236 ),
237 }
238 }
239
240 fn projection_evaluation(
241 &self,
242 row_range: &Range<u64>,
243 expr: &BoundExpression,
244 mask: MaskFuture,
245 ) -> VortexResult<BoxFuture<'static, VortexResult<ArrayRef>>> {
246 match &self.partition_expr(expr)? {
247 Partitioning::RowIdx(expr) => Ok(row_idx_array_future(
248 self.row_offset,
249 row_range,
250 expr.clone(),
251 mask,
252 )),
253 Partitioning::Child(expr) => self.child.projection_evaluation(row_range, expr, mask),
254 Partitioning::Partitioned(p) => {
255 Arc::clone(p).into_array_future(mask, |annotation, expr, mask| match annotation {
256 Partition::RowIdx => Ok(row_idx_array_future(
257 self.row_offset,
258 row_range,
259 expr.clone(),
260 mask,
261 )),
262 Partition::Child => self.child.projection_evaluation(row_range, expr, mask),
263 })
264 }
265 }
266 }
267
268 fn as_any(&self) -> &dyn std::any::Any {
269 self
270 }
271}
272
273fn replace_row_idx(expr: BoundExpression) -> VortexResult<BoundExpression> {
274 Ok(expr
275 .transform_down(|node| {
276 if node
277 .as_scalar()
278 .is_some_and(|scalar_fn| scalar_fn.is::<RowIdx>())
279 {
280 Ok(Transformed {
281 value: BoundExpression::new_root(row_idx_dtype()),
282 changed: true,
283 order: TraversalOrder::Skip,
284 })
285 } else {
286 Ok(Transformed::no(node))
287 }
288 })?
289 .into_inner())
290}
291
292fn row_idx_dtype() -> DType {
293 DType::Primitive(PType::U64, NonNullable)
294}
295
296fn idx_array(row_offset: u64, row_range: &Range<u64>) -> SequenceArray {
298 Sequence::try_new(
299 PValue::U64(row_offset + row_range.start),
300 PValue::U64(1),
301 PType::U64,
302 NonNullable,
303 usize::try_from(row_range.end - row_range.start)
304 .vortex_expect("Row range length must fit in usize"),
305 )
306 .vortex_expect("Failed to create row index array")
307}
308
309fn row_idx_mask_future(
310 row_offset: u64,
311 row_range: &Range<u64>,
312 expr: BoundExpression,
313 mask: MaskFuture,
314 session: VortexSession,
315) -> MaskFuture {
316 let row_range = row_range.clone();
317 MaskFuture::new(mask.len(), async move {
318 let array = idx_array(row_offset, &row_range).into_array();
319
320 let mut ctx = session.create_execution_ctx();
321 let result_mask = array
322 .apply_bound(&expr)?
323 .fill_null(false)?
324 .execute::<Mask>(&mut ctx)?;
325
326 Ok(result_mask.bitand(&mask.await?))
327 })
328}
329
330fn row_idx_array_future(
331 row_offset: u64,
332 row_range: &Range<u64>,
333 expr: BoundExpression,
334 mask: MaskFuture,
335) -> ArrayFuture {
336 let row_range = row_range.clone();
337 async move {
338 idx_array(row_offset, &row_range)
341 .into_array()
342 .filter(mask.await?)?
343 .apply_bound(&expr)
344 }
345 .boxed()
346}
347
348#[cfg(test)]
349mod tests {
350 use std::sync::Arc;
351
352 use vortex_array::ArrayContext;
353 use vortex_array::IntoArray as _;
354 use vortex_array::MaskFuture;
355 use vortex_array::VortexSessionExecute;
356 use vortex_array::arrays::BoolArray;
357 use vortex_array::assert_arrays_eq;
358 use vortex_array::dtype::DType;
359 use vortex_array::dtype::Nullability;
360 use vortex_array::dtype::PType;
361 use vortex_array::expr::eq;
362 use vortex_array::expr::gt;
363 use vortex_array::expr::lit;
364 use vortex_array::expr::or;
365 use vortex_array::expr::root;
366 use vortex_buffer::buffer;
367 use vortex_io::runtime::single::block_on;
368 use vortex_io::session::RuntimeSessionExt;
369 use vortex_mask::Mask;
370 use vortex_sequence::Sequence;
371
372 use crate::LayoutReader;
373 use crate::LayoutStrategy;
374 use crate::layouts::flat::writer::FlatLayoutStrategy;
375 use crate::layouts::row_idx::RowIdxLayoutReader;
376 use crate::layouts::row_idx::row_idx;
377 use crate::segments::TestSegments;
378 use crate::sequence::SequenceId;
379 use crate::sequence::SequentialArrayStreamExt;
380 use crate::test::new_session;
381
382 #[test]
383 fn flat_expr_no_row_id() {
384 block_on(|handle| async {
385 let session = new_session().with_handle(handle);
386 let mut ctx = session.create_execution_ctx();
387 let array_ctx = ArrayContext::empty();
388 let segments = Arc::new(TestSegments::default());
389 let (ptr, eof) = SequenceId::root().split();
390 let array = buffer![1..=5].into_array();
391 let layout = FlatLayoutStrategy::default()
392 .write_stream(
393 array_ctx.into(),
394 Arc::<TestSegments>::clone(&segments),
395 array.to_array_stream().sequenced(ptr),
396 eof,
397 &session,
398 )
399 .await
400 .unwrap();
401
402 let expr = eq(root(), lit(3i32));
403 let reader = RowIdxLayoutReader::new(
404 0,
405 layout
406 .new_reader("".into(), segments, &session, &Default::default())
407 .unwrap(),
408 session.clone(),
409 );
410 let expr = expr.bind(reader.dtype()).unwrap();
411 let result = reader
412 .projection_evaluation(
413 &(0..layout.row_count()),
414 &expr,
415 MaskFuture::new_true(layout.row_count().try_into().unwrap()),
416 )
417 .unwrap()
418 .await
419 .unwrap();
420
421 assert_arrays_eq!(
422 result,
423 BoolArray::from_iter([false, false, true, false, false]),
424 &mut ctx
425 );
426 })
427 }
428
429 #[test]
430 fn flat_expr_row_id() {
431 block_on(|handle| async {
432 let session = new_session().with_handle(handle);
433 let mut ctx = session.create_execution_ctx();
434 let array_ctx = ArrayContext::empty();
435 let segments = Arc::new(TestSegments::default());
436 let (ptr, eof) = SequenceId::root().split();
437 let array = buffer![1..=5].into_array();
438 let layout = FlatLayoutStrategy::default()
439 .write_stream(
440 array_ctx.into(),
441 Arc::<TestSegments>::clone(&segments),
442 array.to_array_stream().sequenced(ptr),
443 eof,
444 &session,
445 )
446 .await
447 .unwrap();
448
449 let expr = gt(row_idx(), lit(3u64));
450 let reader = RowIdxLayoutReader::new(
451 0,
452 layout
453 .new_reader("".into(), segments, &session, &Default::default())
454 .unwrap(),
455 session.clone(),
456 );
457 let expr = expr.bind(reader.dtype()).unwrap();
458 let result = reader
459 .projection_evaluation(
460 &(0..layout.row_count()),
461 &expr,
462 MaskFuture::new_true(layout.row_count().try_into().unwrap()),
463 )
464 .unwrap()
465 .await
466 .unwrap();
467
468 assert_arrays_eq!(
469 result,
470 BoolArray::from_iter([false, false, false, false, true]),
471 &mut ctx
472 );
473 })
474 }
475
476 #[test]
477 fn flat_expr_or() {
478 block_on(|handle| async {
479 let session = new_session().with_handle(handle);
480 let mut ctx = session.create_execution_ctx();
481 let array_ctx = ArrayContext::empty();
482 let segments = Arc::new(TestSegments::default());
483 let (ptr, eof) = SequenceId::root().split();
484 let array = buffer![1..=5].into_array();
485 let layout = FlatLayoutStrategy::default()
486 .write_stream(
487 array_ctx.into(),
488 Arc::<TestSegments>::clone(&segments),
489 array.to_array_stream().sequenced(ptr),
490 eof,
491 &session,
492 )
493 .await
494 .unwrap();
495
496 let expr = or(
497 eq(root(), lit(3i32)),
498 or(gt(row_idx(), lit(3u64)), eq(root(), lit(1i32))),
499 );
500
501 let reader = RowIdxLayoutReader::new(
502 0,
503 layout
504 .new_reader("".into(), segments, &session, &Default::default())
505 .unwrap(),
506 session.clone(),
507 );
508 let expr = expr.bind(reader.dtype()).unwrap();
509 let result = reader
510 .projection_evaluation(
511 &(0..layout.row_count()),
512 &expr,
513 MaskFuture::new_true(layout.row_count().try_into().unwrap()),
514 )
515 .unwrap()
516 .await
517 .unwrap();
518
519 assert_arrays_eq!(
520 result,
521 BoolArray::from_iter([true, false, true, false, true]),
522 &mut ctx
523 );
524 })
525 }
526
527 #[test]
528 fn row_idx_array_all_true_keeps_sequence() {
529 block_on(|_| async {
530 let expr = root()
531 .bind(&DType::Primitive(PType::U64, Nullability::NonNullable))
532 .unwrap();
533
534 let result = super::row_idx_array_future(
535 10,
536 &(2..7),
537 expr,
538 MaskFuture::ready(Mask::new_true(5)),
539 )
540 .await
541 .unwrap();
542
543 assert!(result.is::<Sequence>());
544 assert_arrays_eq!(
545 result,
546 buffer![12u64, 13, 14, 15, 16].into_array(),
547 &mut new_session().create_execution_ctx()
548 );
549 })
550 }
551
552 #[test]
553 fn row_idx_array_all_false_returns_empty() {
554 block_on(|_| async {
555 let expr = root()
556 .bind(&DType::Primitive(PType::U64, Nullability::NonNullable))
557 .unwrap();
558
559 let result = super::row_idx_array_future(
560 10,
561 &(2..7),
562 expr,
563 MaskFuture::ready(Mask::new_false(5)),
564 )
565 .await
566 .unwrap();
567
568 assert_eq!(
569 result.dtype(),
570 &DType::Primitive(PType::U64, Nullability::NonNullable)
571 );
572 assert_eq!(result.len(), 0);
573 })
574 }
575}