|
2 | 2 | // SPDX-FileCopyrightText: Copyright the Vortex contributors |
3 | 3 |
|
4 | 4 | use std::borrow::Cow; |
| 5 | +use std::future; |
| 6 | +use std::ops::Range; |
5 | 7 | use std::sync::Arc; |
6 | 8 |
|
| 9 | +use futures::FutureExt; |
| 10 | +use futures::TryStreamExt; |
| 11 | +use futures::stream::FuturesOrdered; |
| 12 | +use vortex_array::Canonical; |
7 | 13 | use vortex_array::EmptyMetadata; |
| 14 | +use vortex_array::IntoArray; |
| 15 | +use vortex_array::MaskFuture; |
| 16 | +use vortex_array::arrays::ChunkedArray; |
8 | 17 | use vortex_array::dtype::DType; |
9 | 18 | use vortex_array::expr::ExactBoundExpr; |
10 | 19 | use vortex_array::expr::label_bound_tree; |
| 20 | +use vortex_error::VortexExpect; |
11 | 21 | use vortex_error::VortexResult; |
12 | 22 | use vortex_error::vortex_bail; |
| 23 | +use vortex_error::vortex_ensure; |
| 24 | +use vortex_error::vortex_err; |
13 | 25 | use vortex_session::registry::CachedId; |
14 | 26 |
|
15 | 27 | use crate::layouts::row_idx::RowIdx as RowIdxFn; |
16 | 28 | use crate::plan::Eval; |
17 | 29 | use crate::plan::EvalPlan; |
18 | 30 | use crate::plan::Plan; |
| 31 | +use crate::plan::PlanArrayFuture; |
19 | 32 | use crate::plan::PlanChildren; |
| 33 | +use crate::plan::PlanExecutionContext; |
20 | 34 | use crate::plan::PlanId; |
21 | 35 | use crate::plan::PlanParts; |
22 | 36 | use crate::plan::PlanRef; |
@@ -142,6 +156,57 @@ impl PlanVTable for Concat { |
142 | 156 | Ok(()) |
143 | 157 | } |
144 | 158 |
|
| 159 | + fn execute( |
| 160 | + plan: &Plan<Self>, |
| 161 | + ctx: &PlanExecutionContext, |
| 162 | + row_range: &Range<u64>, |
| 163 | + mask: MaskFuture, |
| 164 | + ) -> VortexResult<PlanArrayFuture> { |
| 165 | + vortex_ensure!( |
| 166 | + row_range.start <= row_range.end && row_range.end <= plan.row_count(), |
| 167 | + "Concat row range {:?} is outside 0..{}", |
| 168 | + row_range, |
| 169 | + plan.row_count() |
| 170 | + ); |
| 171 | + vortex_ensure!( |
| 172 | + mask.len() == usize::try_from(row_range.end - row_range.start)?, |
| 173 | + "Concat mask length mismatch" |
| 174 | + ); |
| 175 | + if row_range.is_empty() { |
| 176 | + let empty = Canonical::empty(plan.dtype()).into_array(); |
| 177 | + return Ok(future::ready(Ok(empty)).boxed()); |
| 178 | + } |
| 179 | + |
| 180 | + let mut chunk_futures = Vec::new(); |
| 181 | + for (chunk, &chunk_offset) in plan.children().iter().zip(plan.row_offsets()) { |
| 182 | + let chunk = chunk?; |
| 183 | + let chunk_end = chunk_offset |
| 184 | + .checked_add(chunk.row_count()) |
| 185 | + .ok_or_else(|| vortex_err!("Chunk row offset overflow"))?; |
| 186 | + let start = row_range.start.max(chunk_offset); |
| 187 | + let end = row_range.end.min(chunk_end); |
| 188 | + if start < end { |
| 189 | + let child_range = start - chunk_offset..end - chunk_offset; |
| 190 | + let mask_range = usize::try_from(start - row_range.start)? |
| 191 | + ..usize::try_from(end - row_range.start)?; |
| 192 | + chunk_futures.push(chunk.execute(ctx, &child_range, mask.slice(mask_range))?); |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + Ok(async move { |
| 197 | + let chunks: Vec<_> = FuturesOrdered::from_iter(chunk_futures) |
| 198 | + .try_collect() |
| 199 | + .await?; |
| 200 | + vortex_ensure!(!chunks.is_empty(), "Non-empty row range selected no chunks"); |
| 201 | + if chunks.len() == 1 { |
| 202 | + return Ok(chunks.into_iter().next().vortex_expect("one chunk")); |
| 203 | + } |
| 204 | + let dtype = chunks[0].dtype().clone(); |
| 205 | + Ok(ChunkedArray::try_new(chunks, dtype)?.into_array()) |
| 206 | + } |
| 207 | + .boxed()) |
| 208 | + } |
| 209 | + |
145 | 210 | fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> { |
146 | 211 | Cow::Owned(format!("chunks[{index}]")) |
147 | 212 | } |
|
0 commit comments