Skip to content

Commit 94e0577

Browse files
committed
Add plan-native scan execution
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
1 parent bb1ee5d commit 94e0577

29 files changed

Lines changed: 2403 additions & 22 deletions

Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ members = [
2323
"vortex-btrblocks",
2424
"vortex-layout",
2525
"vortex-scan",
26+
"vortex-scan-v2",
2627
"vortex-file",
2728
"vortex-ipc",
2829
"vortex",
@@ -323,6 +324,7 @@ vortex-proto = { version = "0.1.0", path = "./vortex-proto", default-features =
323324
vortex-row = { version = "0.1.0", path = "./vortex-row", default-features = false }
324325
vortex-runend = { version = "0.1.0", path = "./encodings/runend", default-features = false }
325326
vortex-scan = { version = "0.1.0", path = "./vortex-scan", default-features = false }
327+
vortex-scan-v2 = { version = "0.1.0", path = "./vortex-scan-v2", default-features = false }
326328
vortex-sequence = { version = "0.1.0", path = "encodings/sequence", default-features = false }
327329
vortex-session = { version = "0.1.0", path = "./vortex-session", default-features = false }
328330
vortex-sparse = { version = "0.1.0", path = "./encodings/sparse", default-features = false }

docs/developer-guide/internals/scan-planning.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,14 @@ ID, dtype, row count, and lazy children. Only the unsized tail containing the vt
5555
already serialize their metadata; the ones holding a read context or a bound expression return
5656
`None` until those codecs exist.
5757

58+
## Execution
59+
60+
Each operator executes over a row range and selection mask. `SegmentScan` reads its segment,
61+
structural operators combine their children, and `Eval` applies the remaining derived work.
62+
`vortex-scan-v2` copies the existing scan orchestration around this API, so the original
63+
`LayoutReader` scanner is untouched while the plan-native path is developed.
64+
5865
## Future work
5966

60-
Plans currently stop at construction and optimization. Still to come: a plan registry and foreign
61-
operator placeholder so third-party operators survive a round trip, a serialization envelope, and
62-
an execution stage that walks an optimized plan, reads the referenced segments, and returns the
63-
query result.
67+
Still to come: a plan registry and foreign operator placeholder so third-party operators survive
68+
a round trip, and a serialization envelope.

vortex-layout/src/layouts/row_idx/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ fn row_idx_dtype() -> DType {
297297
}
298298

299299
// Returns a SequenceArray representing the row indices for the given row range,
300-
fn idx_array(row_offset: u64, row_range: &Range<u64>) -> SequenceArray {
300+
pub(crate) fn idx_array(row_offset: u64, row_range: &Range<u64>) -> SequenceArray {
301301
Sequence::try_new(
302302
PValue::U64(row_offset + row_range.start),
303303
PValue::U64(1),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::sync::Arc;
5+
6+
use futures::future::BoxFuture;
7+
use vortex_array::ArrayRef;
8+
use vortex_error::VortexResult;
9+
use vortex_session::VortexSession;
10+
11+
use crate::segments::SegmentSource;
12+
13+
/// Future resolving to the array produced by a physical plan.
14+
pub type PlanArrayFuture = BoxFuture<'static, VortexResult<ArrayRef>>;
15+
16+
/// Runtime dependencies shared by every node in a plan execution.
17+
#[derive(Clone)]
18+
pub struct PlanExecutionContext {
19+
segment_source: Arc<dyn SegmentSource>,
20+
session: VortexSession,
21+
}
22+
23+
impl PlanExecutionContext {
24+
/// Creates an execution context over a segment source and Vortex session.
25+
pub fn new(segment_source: Arc<dyn SegmentSource>, session: VortexSession) -> Self {
26+
Self {
27+
segment_source,
28+
session,
29+
}
30+
}
31+
32+
/// Returns the segment source used to satisfy leaf reads.
33+
pub fn segment_source(&self) -> &Arc<dyn SegmentSource> {
34+
&self.segment_source
35+
}
36+
37+
/// Returns the Vortex session used for array decoding and expression execution.
38+
pub fn session(&self) -> &VortexSession {
39+
&self.session
40+
}
41+
}

vortex-layout/src/plan/lower.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn lower_dict(layout: &DictLayout) -> VortexResult<TakePlan> {
126126
TakePlan::from_children_unchecked(
127127
layout.dtype().clone(),
128128
layout.row_count(),
129+
layout.has_all_values_referenced(),
129130
lazy_children(layout.to_layout(), vec![1, 0]),
130131
)
131132
})

vortex-layout/src/plan/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
1010
mod children;
1111
mod display;
12+
mod execution;
1213
mod lower;
1314
mod optimize;
1415
pub mod optimizer;
@@ -22,6 +23,8 @@ pub use display::PlanSummaryExtractor;
2223
pub use display::PlanTreeContext;
2324
pub use display::PlanTreeDisplay;
2425
pub use display::PlanTreeExtractor;
26+
pub use execution::PlanArrayFuture;
27+
pub use execution::PlanExecutionContext;
2528
pub use lower::lower;
2629
pub use optimize::optimize;
2730
pub use plans::Concat;
@@ -38,15 +41,23 @@ pub use plans::PackData;
3841
pub use plans::PackPlan;
3942
pub use plans::RowIdx;
4043
pub use plans::RowIdxData;
44+
pub use plans::RowIdxPartition;
45+
pub use plans::RowIdxPartitionPlan;
4146
pub use plans::RowIdxPlan;
4247
pub use plans::RowIdxPlanMetadata;
48+
pub use plans::RowIdxValues;
49+
pub use plans::RowIdxValuesData;
50+
pub use plans::RowIdxValuesPlan;
51+
pub use plans::RowIdxValuesPlanMetadata;
4352
pub use plans::SegmentScan;
4453
pub use plans::SegmentScanData;
4554
pub use plans::SegmentScanPlan;
4655
pub use plans::Take;
56+
pub use plans::TakeData;
4757
pub use plans::TakePlan;
4858
pub use plans::Zoned;
4959
pub use plans::ZonedPlan;
60+
pub use plans::row_idx_dtype;
5061
pub use typed::DynPlan;
5162
pub use typed::Plan;
5263
pub use typed::PlanParts;

vortex-layout/src/plan/plans/concat.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,35 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
use std::borrow::Cow;
5+
use std::future;
6+
use std::ops::Range;
57
use std::sync::Arc;
68

9+
use futures::FutureExt;
10+
use futures::TryStreamExt;
11+
use futures::stream::FuturesOrdered;
12+
use vortex_array::Canonical;
713
use vortex_array::EmptyMetadata;
14+
use vortex_array::IntoArray;
15+
use vortex_array::MaskFuture;
16+
use vortex_array::arrays::ChunkedArray;
817
use vortex_array::dtype::DType;
918
use vortex_array::expr::ExactBoundExpr;
1019
use vortex_array::expr::label_bound_tree;
20+
use vortex_error::VortexExpect;
1121
use vortex_error::VortexResult;
1222
use vortex_error::vortex_bail;
23+
use vortex_error::vortex_ensure;
24+
use vortex_error::vortex_err;
1325
use vortex_session::registry::CachedId;
1426

1527
use crate::layouts::row_idx::RowIdx as RowIdxFn;
1628
use crate::plan::Eval;
1729
use crate::plan::EvalPlan;
1830
use crate::plan::Plan;
31+
use crate::plan::PlanArrayFuture;
1932
use crate::plan::PlanChildren;
33+
use crate::plan::PlanExecutionContext;
2034
use crate::plan::PlanId;
2135
use crate::plan::PlanParts;
2236
use crate::plan::PlanRef;
@@ -142,6 +156,57 @@ impl PlanVTable for Concat {
142156
Ok(())
143157
}
144158

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+
145210
fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
146211
Cow::Owned(format!("chunks[{index}]"))
147212
}

vortex-layout/src/plan/plans/eval.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
use std::borrow::Cow;
55
use std::fmt;
6+
use std::ops::Range;
67

8+
use futures::FutureExt;
79
use vortex_array::EmptyMetadata;
10+
use vortex_array::MaskFuture;
811
use vortex_array::dtype::DType;
912
use vortex_array::dtype::FieldName;
1013
use vortex_array::expr::BoundExpression;
@@ -18,7 +21,9 @@ use vortex_error::vortex_bail;
1821
use vortex_session::registry::CachedId;
1922

2023
use crate::plan::Plan;
24+
use crate::plan::PlanArrayFuture;
2125
use crate::plan::PlanChildren;
26+
use crate::plan::PlanExecutionContext;
2227
use crate::plan::PlanId;
2328
use crate::plan::PlanParts;
2429
use crate::plan::PlanRef;
@@ -113,6 +118,17 @@ impl PlanVTable for Eval {
113118
Ok(())
114119
}
115120

121+
fn execute(
122+
plan: &Plan<Self>,
123+
ctx: &PlanExecutionContext,
124+
row_range: &Range<u64>,
125+
mask: MaskFuture,
126+
) -> VortexResult<PlanArrayFuture> {
127+
let child = plan.child_plan()?.execute(ctx, row_range, mask)?;
128+
let expression = plan.expression().clone();
129+
Ok(async move { child.await?.apply_bound(&expression) }.boxed())
130+
}
131+
116132
fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
117133
if index == 0 {
118134
Cow::Borrowed("child")

0 commit comments

Comments
 (0)