Skip to content

Commit aa80921

Browse files
committed
Add zoned layout scan plan
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
1 parent bda6747 commit aa80921

4 files changed

Lines changed: 171 additions & 0 deletions

File tree

vortex-layout/src/plan/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub use plans::RowIdxPartitionPlan;
2828
pub use plans::RowIdxPlan;
2929
pub use plans::RowIdxValuesPlan;
3030
pub use plans::StructPlan;
31+
pub use plans::ZonedPlan;
3132
use vortex_array::dtype::DType;
3233
use vortex_error::VortexResult;
3334
use vortex_error::vortex_bail;
@@ -38,6 +39,8 @@ use crate::layouts::dict::Dict;
3839
use crate::layouts::flat::Flat;
3940
use crate::layouts::list::List;
4041
use crate::layouts::struct_::Struct;
42+
use crate::layouts::zoned::LegacyStats;
43+
use crate::layouts::zoned::Zoned;
4144

4245
/// Shared handle to a heap-allocated physical plan.
4346
pub type PlanRef = Arc<dyn Plan>;
@@ -103,6 +106,9 @@ pub fn new_plan(layout: &LayoutRef) -> VortexResult<PlanRef> {
103106
if let Some(layout) = layout.as_opt::<Struct>() {
104107
return Ok(Arc::new(StructPlan::new(layout)));
105108
}
109+
if layout.is::<Zoned>() || layout.is::<LegacyStats>() {
110+
return Ok(Arc::new(ZonedPlan::try_new(layout)?));
111+
}
106112
vortex_bail!(
107113
"No physical plan implementation for layout '{}'",
108114
layout.encoding_id()

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod flat;
88
mod list;
99
mod row_idx;
1010
mod struct_;
11+
mod zoned;
1112

1213
pub use chunked::ChunkedPlan;
1314
pub(crate) use chunked::ExpressionChunkedRule;
@@ -22,3 +23,4 @@ pub use row_idx::RowIdxPlan;
2223
pub use row_idx::RowIdxValuesPlan;
2324
pub(crate) use struct_::ExpressionStructRule;
2425
pub use struct_::StructPlan;
26+
pub use zoned::ZonedPlan;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::borrow::Cow;
5+
use std::sync::Arc;
6+
7+
use vortex_array::dtype::DType;
8+
use vortex_error::VortexResult;
9+
use vortex_error::vortex_bail;
10+
use vortex_error::vortex_err;
11+
12+
use crate::LayoutRef;
13+
use crate::plan::Plan;
14+
use crate::plan::PlanRef;
15+
use crate::plan::new_plan;
16+
17+
const DATA_CHILD_INDEX: usize = 0;
18+
const ZONES_CHILD_INDEX: usize = 1;
19+
20+
/// A physical zoned plan with a transparent data child and an auxiliary zones child.
21+
///
22+
/// This plan represents both current `vortex.zoned` layouts and legacy `vortex.stats` layouts,
23+
/// which have the same physical child shape.
24+
pub struct ZonedPlan {
25+
layout: LayoutRef,
26+
dtype: DType,
27+
data: PlanRef,
28+
zones: PlanRef,
29+
}
30+
31+
impl ZonedPlan {
32+
pub(crate) fn try_new(layout: &LayoutRef) -> VortexResult<Self> {
33+
let data = new_plan(
34+
&layout
35+
.slot(DATA_CHILD_INDEX)?
36+
.ok_or_else(|| vortex_err!("Zoned data child is absent"))?,
37+
)?;
38+
let zones = new_plan(
39+
&layout
40+
.slot(ZONES_CHILD_INDEX)?
41+
.ok_or_else(|| vortex_err!("Zoned zones child is absent"))?,
42+
)?;
43+
Ok(Self {
44+
layout: Arc::clone(layout),
45+
dtype: layout.dtype().clone(),
46+
data,
47+
zones,
48+
})
49+
}
50+
51+
fn with_children(&self, data: PlanRef, zones: PlanRef) -> Self {
52+
Self {
53+
layout: Arc::clone(&self.layout),
54+
dtype: self.dtype.clone(),
55+
data,
56+
zones,
57+
}
58+
}
59+
}
60+
61+
impl Plan for ZonedPlan {
62+
fn name(&self) -> &'static str {
63+
"ZonedPlan"
64+
}
65+
66+
fn optimize(&self) -> VortexResult<PlanRef> {
67+
let data = self.data.optimize()?;
68+
let zones = self.zones.optimize()?;
69+
Ok(Arc::new(self.with_children(data, zones)))
70+
}
71+
72+
fn dtype(&self) -> &DType {
73+
&self.dtype
74+
}
75+
76+
fn row_count(&self) -> u64 {
77+
self.layout.row_count()
78+
}
79+
80+
fn child_count(&self) -> usize {
81+
2
82+
}
83+
84+
fn child(&self, index: usize) -> VortexResult<Option<PlanRef>> {
85+
match index {
86+
DATA_CHILD_INDEX => Ok(Some(Arc::clone(&self.data))),
87+
ZONES_CHILD_INDEX => Ok(Some(Arc::clone(&self.zones))),
88+
_ => vortex_bail!("Zoned plan has no child {index}"),
89+
}
90+
}
91+
92+
fn child_name(&self, index: usize) -> Cow<'_, str> {
93+
match index {
94+
DATA_CHILD_INDEX => Cow::Borrowed("data"),
95+
ZONES_CHILD_INDEX => Cow::Borrowed("zones"),
96+
_ => Cow::Owned(format!("child[{index}]")),
97+
}
98+
}
99+
}

vortex-layout/src/plan/tests.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
use std::num::NonZeroUsize;
45
use std::sync::Arc;
56

7+
use vortex_array::aggregate_fn::AggregateFnRef;
68
use vortex_array::dtype::DType;
79
use vortex_array::dtype::Nullability;
810
use vortex_array::dtype::PType;
@@ -20,6 +22,8 @@ use vortex_session::registry::CachedId;
2022
use vortex_session::registry::ReadContext;
2123

2224
use super::*;
25+
use crate::LayoutBuildContext;
26+
use crate::LayoutEncoding;
2327
use crate::LayoutRef;
2428
use crate::OwnedLayoutChildren;
2529
use crate::layouts::chunked::ChunkedLayout;
@@ -28,6 +32,8 @@ use crate::layouts::flat::FlatLayout;
2832
use crate::layouts::foreign::new_foreign_layout;
2933
use crate::layouts::row_idx::row_idx;
3034
use crate::layouts::struct_::StructLayout;
35+
use crate::layouts::zoned::LegacyStatsLayoutEncoding;
36+
use crate::layouts::zoned::ZonedLayout;
3137
use crate::segments::SegmentId;
3238

3339
fn primitive(ptype: PType, nullability: Nullability) -> DType {
@@ -53,6 +59,64 @@ fn make_plan(layout: LayoutRef) -> VortexResult<PlanRef> {
5359
new_plan(&layout)
5460
}
5561

62+
#[test]
63+
fn zoned_plan_exposes_data_and_zones() -> VortexResult<()> {
64+
let dtype = primitive(PType::I32, Nullability::NonNullable);
65+
let zones_dtype = DType::Struct(StructFields::empty(), Nullability::NonNullable);
66+
let zone_len = NonZeroUsize::new(3).ok_or_else(|| vortex_err!("zone length is zero"))?;
67+
let aggregate_fns: Arc<[AggregateFnRef]> = Vec::new().into();
68+
let layout = ZonedLayout::try_new(
69+
flat(5, dtype, 0),
70+
flat(2, zones_dtype, 1),
71+
zone_len,
72+
aggregate_fns,
73+
)?
74+
.into_layout();
75+
76+
let plan = make_plan(layout)?;
77+
assert!(plan.is::<ZonedPlan>());
78+
insta::assert_snapshot!(plan.tree_display(), @r"
79+
root: ZonedPlan(i32, rows=5)
80+
data: FlatPlan(i32, rows=5)
81+
zones: FlatPlan({}, rows=2)
82+
");
83+
Ok(())
84+
}
85+
86+
#[test]
87+
fn legacy_stats_layout_uses_zoned_plan() -> VortexResult<()> {
88+
let dtype = primitive(PType::I32, Nullability::NonNullable);
89+
let zones_dtype = DType::Struct(StructFields::empty(), Nullability::NonNullable);
90+
let children = OwnedLayoutChildren::layout_children(vec![
91+
flat(5, dtype.clone(), 0),
92+
flat(2, zones_dtype, 1),
93+
]);
94+
let session = vortex_array::array_session();
95+
let read_ctx = ReadContext::new([]);
96+
let build_ctx = LayoutBuildContext {
97+
session: &session,
98+
array_read_ctx: &read_ctx,
99+
};
100+
let layout = LayoutEncoding::build(
101+
&LegacyStatsLayoutEncoding,
102+
&dtype,
103+
5,
104+
&3_u32.to_le_bytes(),
105+
Vec::new(),
106+
children.as_ref(),
107+
&build_ctx,
108+
)?;
109+
110+
let plan = make_plan(layout)?;
111+
assert!(plan.is::<ZonedPlan>());
112+
insta::assert_snapshot!(plan.tree_display(), @r"
113+
root: ZonedPlan(i32, rows=5)
114+
data: FlatPlan(i32, rows=5)
115+
zones: FlatPlan({}, rows=2)
116+
");
117+
Ok(())
118+
}
119+
56120
#[test]
57121
fn struct_plan_optimization_visits_all_fields() -> VortexResult<()> {
58122
let field_dtype = primitive(PType::I32, Nullability::NonNullable);

0 commit comments

Comments
 (0)