vortex_layout/plan/plans/
concat.rs1use std::borrow::Cow;
5use std::sync::Arc;
6
7use vortex_array::EmptyMetadata;
8use vortex_array::dtype::DType;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_session::registry::CachedId;
12
13use crate::plan::Eval;
14use crate::plan::EvalPlan;
15use crate::plan::Plan;
16use crate::plan::PlanChildren;
17use crate::plan::PlanId;
18use crate::plan::PlanParts;
19use crate::plan::PlanRef;
20use crate::plan::PlanVTable;
21use crate::plan::optimizer::PlanParentReduceRule;
22
23#[derive(Clone, Debug)]
25pub struct Concat;
26
27#[derive(Clone, Debug)]
29pub struct ConcatData {
30 row_offsets: Arc<[u64]>,
31}
32
33pub type ConcatPlan = Plan<Concat>;
35
36impl ConcatPlan {
37 pub(crate) unsafe fn from_children_unchecked(
44 dtype: DType,
45 row_count: u64,
46 row_offsets: Arc<[u64]>,
47 children: PlanChildren,
48 ) -> Self {
49 PlanParts {
50 vtable: Concat,
51 dtype,
52 row_count,
53 children,
54 data: ConcatData { row_offsets },
55 }
56 .into_typed()
57 }
58
59 pub fn try_new(dtype: DType, children: Vec<PlanRef>) -> VortexResult<Self> {
63 let mut row_offsets = Vec::with_capacity(children.len());
64 let mut row_count = 0u64;
65 for child in &children {
66 if child.dtype() != &dtype {
67 vortex_bail!(
68 "Concat child dtype {} does not match {dtype}",
69 child.dtype()
70 );
71 }
72 row_offsets.push(row_count);
73 row_count = row_count
74 .checked_add(child.row_count())
75 .ok_or_else(|| vortex_error::vortex_err!("Concat row count overflow"))?;
76 }
77 Ok(unsafe {
79 Self::from_children_unchecked(dtype, row_count, row_offsets.into(), children.into())
80 })
81 }
82
83 pub fn row_offsets(&self) -> &[u64] {
85 &self.data().row_offsets
86 }
87}
88
89impl PlanVTable for Concat {
90 type PlanData = ConcatData;
91 type Metadata = EmptyMetadata;
92
93 fn id(&self) -> PlanId {
94 static ID: CachedId = CachedId::new("vortex.plan.concat");
95 *ID
96 }
97
98 fn metadata(_plan: &Plan<Self>) -> Option<Self::Metadata> {
99 Some(EmptyMetadata)
101 }
102
103 fn with_children(
104 plan: &Plan<Self>,
105 children: &PlanChildren,
106 data: &mut Self::PlanData,
107 ) -> VortexResult<()> {
108 if children.len() != plan.children().len() {
109 vortex_bail!(
110 "Concat expects {} children but got {}",
111 plan.children().len(),
112 children.len()
113 );
114 }
115
116 let mut row_offsets = Vec::with_capacity(children.len());
117 let mut row_count = 0u64;
118 for child in children.iter() {
119 let child = child?;
120 if child.dtype() != plan.dtype() {
121 vortex_bail!(
122 "Concat child dtype {} does not match {}",
123 child.dtype(),
124 plan.dtype()
125 );
126 }
127 row_offsets.push(row_count);
128 row_count = row_count
129 .checked_add(child.row_count())
130 .ok_or_else(|| vortex_error::vortex_err!("Concat row count overflow"))?;
131 }
132 if row_count != plan.row_count() {
133 vortex_bail!(
134 "Concat children have {row_count} rows but the plan has {}",
135 plan.row_count()
136 );
137 }
138 data.row_offsets = row_offsets.into();
139 Ok(())
140 }
141
142 fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
143 Cow::Owned(format!("chunks[{index}]"))
144 }
145}
146
147#[derive(Debug)]
149pub(crate) struct ExpressionConcatRule;
150
151impl PlanParentReduceRule<Concat> for ExpressionConcatRule {
152 type Parent = Eval;
153
154 fn reduce_parent(
155 &self,
156 child: &Plan<Concat>,
157 parent: &Plan<Eval>,
158 _child_idx: usize,
159 ) -> VortexResult<Option<PlanRef>> {
160 let expression = parent.expression();
161 let chunks = child
162 .children()
163 .iter()
164 .map(|chunk| Ok(EvalPlan::try_new(expression.clone(), chunk?)?.into_plan()))
165 .collect::<VortexResult<Vec<_>>>()?;
166 Ok(Some(
167 ConcatPlan::try_new(expression.dtype().clone(), chunks)?.into_plan(),
168 ))
169 }
170}