vortex_layout/plan/physical/plans/
list_pack.rs1use std::borrow::Cow;
5use std::sync::Arc;
6
7use vortex_array::EmptyMetadata;
8use vortex_array::dtype::DType;
9use vortex_array::dtype::Nullability;
10use vortex_error::VortexResult;
11use vortex_error::vortex_bail;
12use vortex_error::vortex_err;
13use vortex_session::registry::CachedId;
14
15use crate::plan::physical::Plan;
16use crate::plan::physical::PlanChildren;
17use crate::plan::physical::PlanId;
18use crate::plan::physical::PlanParts;
19use crate::plan::physical::PlanRef;
20use crate::plan::physical::PlanVTable;
21
22const ELEMENTS: usize = 0;
23const OFFSETS: usize = 1;
24const VALIDITY: usize = 2;
25
26#[derive(Clone, Debug)]
28pub struct ListPack;
29
30#[derive(Clone, Debug)]
32pub struct ListPackData;
33
34pub type ListPackPlan = Plan<ListPack>;
36
37impl ListPackPlan {
38 pub(crate) unsafe fn from_children_unchecked(
46 dtype: DType,
47 row_count: u64,
48 children: PlanChildren,
49 ) -> Self {
50 PlanParts {
51 vtable: ListPack,
52 dtype,
53 row_count,
54 children,
55 data: ListPackData,
56 }
57 .into_typed()
58 }
59
60 pub fn try_new(
65 nullability: Nullability,
66 row_count: u64,
67 elements: PlanRef,
68 offsets: PlanRef,
69 validity: Option<PlanRef>,
70 ) -> VortexResult<Self> {
71 let dtype = DType::List(Arc::new(elements.dtype().clone()), nullability);
72 let mut children = vec![elements, offsets];
73 children.extend(validity);
74 let children = PlanChildren::from(children);
75 validate_children(&dtype, row_count, &children)?;
76
77 Ok(unsafe { Self::from_children_unchecked(dtype, row_count, children) })
79 }
80
81 pub fn elements(&self) -> VortexResult<PlanRef> {
83 self.child_required(ELEMENTS)
84 }
85
86 pub fn offsets(&self) -> VortexResult<PlanRef> {
88 self.child_required(OFFSETS)
89 }
90
91 pub fn validity(&self) -> VortexResult<Option<PlanRef>> {
93 self.child(VALIDITY)
94 }
95}
96
97impl PlanVTable for ListPack {
98 type PlanData = ListPackData;
99 type Metadata = EmptyMetadata;
100
101 fn id(&self) -> PlanId {
102 static ID: CachedId = CachedId::new("vortex.plan.list_pack");
103 *ID
104 }
105
106 fn metadata(_plan: &Plan<Self>) -> Option<Self::Metadata> {
107 Some(EmptyMetadata)
109 }
110
111 fn with_children(
112 plan: &Plan<Self>,
113 children: &PlanChildren,
114 _data: &mut Self::PlanData,
115 ) -> VortexResult<()> {
116 validate_children(plan.dtype(), plan.row_count(), children)
117 }
118
119 fn child_name(_plan: &Plan<Self>, index: usize) -> Cow<'_, str> {
120 match index {
121 ELEMENTS => Cow::Borrowed("elements"),
122 OFFSETS => Cow::Borrowed("offsets"),
123 VALIDITY => Cow::Borrowed("validity"),
124 _ => Cow::Owned(format!("child[{index}]")),
125 }
126 }
127}
128
129fn validate_children(dtype: &DType, row_count: u64, children: &PlanChildren) -> VortexResult<()> {
130 let elements_dtype = dtype
131 .as_list_element_opt()
132 .ok_or_else(|| vortex_err!("ListPack output dtype must be a list, got {dtype}"))?;
133 let expected_children = 2 + usize::from(dtype.is_nullable());
134 if children.len() != expected_children {
135 vortex_bail!(
136 "ListPack expects {expected_children} children but got {}",
137 children.len()
138 );
139 }
140
141 let elements = children
142 .get(ELEMENTS)?
143 .ok_or_else(|| vortex_err!("ListPack elements child is absent"))?;
144 if elements.dtype() != elements_dtype.as_ref() {
145 vortex_bail!(
146 "ListPack elements child has dtype {} but the list element dtype is {}",
147 elements.dtype(),
148 elements_dtype
149 );
150 }
151
152 let offsets = children
153 .get(OFFSETS)?
154 .ok_or_else(|| vortex_err!("ListPack offsets child is absent"))?;
155 if !offsets.dtype().is_int() || offsets.dtype().is_nullable() {
156 vortex_bail!(
157 "ListPack offsets child must have a non-nullable integer dtype, got {}",
158 offsets.dtype()
159 );
160 }
161 let offsets_row_count = row_count
162 .checked_add(1)
163 .ok_or_else(|| vortex_err!("ListPack offsets row count overflow"))?;
164 if offsets.row_count() != offsets_row_count {
165 vortex_bail!(
166 "ListPack offsets child has {} rows but must have {offsets_row_count}",
167 offsets.row_count()
168 );
169 }
170
171 if dtype.is_nullable() {
172 let validity = children
173 .get(VALIDITY)?
174 .ok_or_else(|| vortex_err!("ListPack validity child is absent"))?;
175 let validity_dtype = DType::Bool(Nullability::NonNullable);
176 if validity.dtype() != &validity_dtype {
177 vortex_bail!(
178 "ListPack validity child has dtype {} but must have dtype {validity_dtype}",
179 validity.dtype()
180 );
181 }
182 if validity.row_count() != row_count {
183 vortex_bail!(
184 "ListPack validity child has {} rows but the plan has {row_count}",
185 validity.row_count()
186 );
187 }
188 }
189 Ok(())
190}