vortex_layout/plan/physical/plans/
legacy_stats.rs1use std::borrow::Cow;
5use std::ops::Range;
6use std::sync::Arc;
7
8use vortex_array::EmptyMetadata;
9use vortex_array::dtype::DType;
10use vortex_array::expr::stats::Stat;
11use vortex_error::VortexResult;
12use vortex_error::vortex_bail;
13use vortex_error::vortex_err;
14use vortex_mask::Mask;
15use vortex_session::registry::CachedId;
16
17use crate::layouts::zoned::legacy_stats_to_aggregates;
18use crate::plan::physical::EvalPlan;
19use crate::plan::physical::Plan;
20use crate::plan::physical::PlanChildren;
21use crate::plan::physical::PlanId;
22use crate::plan::physical::PlanParts;
23use crate::plan::physical::PlanRef;
24use crate::plan::physical::PlanVTable;
25use crate::plan::physical::Zoned;
26use crate::plan::physical::ZonedPlan;
27use crate::plan::physical::check_child_count;
28use crate::plan::physical::optimizer::PlanParentReduceRule;
29use crate::plan::physical::optimizer::PlanReduceRule;
30use crate::plan::physical::pipeline::Chain;
31use crate::plan::physical::pipeline::Compiler;
32use crate::plan::physical::pipeline::Reach;
33use crate::segments::SegmentId;
34
35const DATA: usize = 0;
36const ZONES: usize = 1;
37
38#[derive(Clone, Debug)]
45pub struct LegacyStats;
46
47#[derive(Clone, Debug)]
49pub struct LegacyStatsData {
50 zone_len: u64,
51 stats: Arc<[Stat]>,
53}
54
55pub type LegacyStatsPlan = Plan<LegacyStats>;
57
58impl LegacyStatsPlan {
59 pub(crate) fn from_children(
60 dtype: DType,
61 row_count: u64,
62 children: PlanChildren,
63 zone_len: u64,
64 stats: Arc<[Stat]>,
65 ) -> Self {
66 PlanParts {
67 vtable: LegacyStats,
68 dtype,
69 row_count,
70 children,
71 data: LegacyStatsData { zone_len, stats },
72 }
73 .into_typed()
74 }
75
76 pub fn new(data: PlanRef, zones: PlanRef, zone_len: u64, stats: Arc<[Stat]>) -> Self {
79 let dtype = data.dtype().clone();
80 let row_count = data.row_count();
81 Self::from_children(dtype, row_count, vec![data, zones].into(), zone_len, stats)
82 }
83
84 pub fn zone_len(&self) -> u64 {
86 self.data().zone_len
87 }
88
89 pub fn stats(&self) -> &Arc<[Stat]> {
91 &self.data().stats
92 }
93
94 pub fn data_plan(&self) -> VortexResult<PlanRef> {
96 self.child_required(DATA)
97 }
98
99 pub fn zones_plan(&self) -> VortexResult<PlanRef> {
101 self.child_required(ZONES)
102 }
103}
104
105impl PlanVTable for LegacyStats {
106 type PlanData = LegacyStatsData;
107 type Metadata = EmptyMetadata;
108
109 fn id(&self) -> PlanId {
110 static ID: CachedId = CachedId::new("vortex.plan.legacy_stats");
111 *ID
112 }
113
114 fn metadata(_plan: &Plan<Self>) -> Option<Self::Metadata> {
115 None
116 }
117
118 fn with_children(
119 plan: &Plan<Self>,
120 children: &PlanChildren,
121 _data: &mut Self::PlanData,
122 ) -> VortexResult<()> {
123 check_child_count("LegacyStats", children, 2)?;
124 let data = children
125 .get(DATA)?
126 .ok_or_else(|| vortex_err!("LegacyStats data child is absent"))?;
127 if data.dtype() != plan.dtype() || data.row_count() != plan.row_count() {
128 vortex_bail!("LegacyStats data child shape does not match the plan output");
129 }
130 Ok(())
131 }
132
133 fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
134 match index {
135 DATA => Cow::Borrowed("data"),
136 ZONES => Cow::Borrowed("zones"),
137 _ => Cow::Owned(format!("child[{index}]")),
138 }
139 }
140
141 fn compile(
142 plan: &Plan<Self>,
143 rows: Range<u64>,
144 mask: &Mask,
145 compiler: &mut Compiler<'_>,
146 ) -> VortexResult<Option<Chain>> {
147 compiler.compile(&plan.data_plan()?, rows, mask)
148 }
149
150 fn reach(
151 plan: &Plan<Self>,
152 rows: Range<u64>,
153 at: &Reach,
154 visit: &mut dyn FnMut(SegmentId, Range<u64>),
155 ) -> VortexResult<()> {
156 plan.data_plan()?.reach(rows, at, visit)
157 }
158}
159
160#[derive(Debug)]
167pub(crate) struct LegacyStatsRule;
168
169impl PlanReduceRule<LegacyStats> for LegacyStatsRule {
170 fn reduce(&self, plan: &Plan<LegacyStats>) -> VortexResult<Option<PlanRef>> {
171 let data = plan.data_plan()?;
172 if plan.zone_len() == 0 || data.is::<Zoned>() {
173 return Ok(Some(data));
174 }
175 let zones = plan.zones_plan()?;
176 let (aggregate_fns, conversion) =
177 legacy_stats_to_aggregates(plan.dtype(), zones.dtype(), plan.stats());
178 if aggregate_fns.is_empty() {
179 return Ok(Some(data));
180 }
181 let zones = EvalPlan::try_new(conversion.bind(zones.dtype())?, zones)?.into_plan();
182 let zoned = ZonedPlan::new(data, zones, plan.zone_len(), aggregate_fns);
183 Ok(Some(zoned.into_plan()))
184 }
185}
186
187#[derive(Debug)]
190pub(crate) struct ZonedLegacyStatsRule;
191
192impl PlanParentReduceRule<LegacyStats> for ZonedLegacyStatsRule {
193 type Parent = Zoned;
194
195 fn reduce_parent(
196 &self,
197 child: &LegacyStatsPlan,
198 parent: &ZonedPlan,
199 child_idx: usize,
200 ) -> VortexResult<Option<PlanRef>> {
201 if child_idx != DATA || parent.is_pruning() {
202 return Ok(None);
203 }
204 let children = vec![child.data_plan()?, parent.zones_plan()?];
205 Ok(Some(parent.clone().into_plan().with_children(children)?))
206 }
207}