vortex_layout/layouts/struct_/
mod.rs1mod reader;
5pub mod writer;
6
7use std::sync::Arc;
8
9use reader::StructReader;
10use vortex_array::DeserializeMetadata;
11use vortex_array::EmptyMetadata;
12use vortex_array::dtype::DType;
13use vortex_array::dtype::Field;
14use vortex_array::dtype::FieldMask;
15use vortex_array::dtype::Nullability;
16use vortex_array::dtype::StructFields;
17use vortex_error::VortexExpect;
18use vortex_error::VortexResult;
19use vortex_error::vortex_bail;
20use vortex_error::vortex_ensure;
21use vortex_error::vortex_err;
22use vortex_session::SessionExt;
23use vortex_session::VortexSession;
24use vortex_session::registry::CachedId;
25pub use writer::StructStrategy;
26
27use crate::LayoutBuildContext;
28use crate::LayoutChildType;
29use crate::LayoutEncodingRef;
30use crate::LayoutId;
31use crate::LayoutReaderRef;
32use crate::LayoutRef;
33use crate::VTable;
34use crate::children::LayoutChildren;
35use crate::children::OwnedLayoutChildren;
36use crate::segments::SegmentId;
37use crate::segments::SegmentSource;
38use crate::vtable;
39
40vtable!(Struct);
41
42impl VTable for Struct {
43 type Layout = StructLayout;
44 type Encoding = StructLayoutEncoding;
45 type Metadata = EmptyMetadata;
46
47 fn id(_encoding: &Self::Encoding) -> LayoutId {
48 static ID: CachedId = CachedId::new("vortex.struct");
49 *ID
50 }
51
52 fn encoding(_layout: &Self::Layout) -> LayoutEncodingRef {
53 LayoutEncodingRef::new_ref(StructLayoutEncoding.as_ref())
54 }
55
56 fn row_count(layout: &Self::Layout) -> u64 {
57 layout.row_count
58 }
59
60 fn dtype(layout: &Self::Layout) -> &DType {
61 &layout.dtype
62 }
63
64 fn metadata(_layout: &Self::Layout) -> Self::Metadata {
65 EmptyMetadata
66 }
67
68 fn segment_ids(_layout: &Self::Layout) -> Vec<SegmentId> {
69 vec![]
70 }
71
72 fn nchildren(layout: &Self::Layout) -> usize {
73 let validity_children = if layout.dtype.is_nullable() { 1 } else { 0 };
74 layout.struct_fields().nfields() + validity_children
75 }
76
77 fn child(layout: &Self::Layout, index: usize) -> VortexResult<LayoutRef> {
78 let schema_index = if layout.dtype.is_nullable() {
79 index.saturating_sub(1)
80 } else {
81 index
82 };
83
84 let child_dtype = if index == 0 && layout.dtype.is_nullable() {
85 DType::Bool(Nullability::NonNullable)
86 } else {
87 layout
88 .struct_fields()
89 .field_by_index(schema_index)
90 .ok_or_else(|| vortex_err!("Missing field {schema_index}"))?
91 };
92
93 layout.children.child(index, &child_dtype)
94 }
95
96 fn child_type(layout: &Self::Layout, idx: usize) -> LayoutChildType {
97 let schema_index = if layout.dtype.is_nullable() {
98 idx.saturating_sub(1)
99 } else {
100 idx
101 };
102
103 if idx == 0 && layout.dtype.is_nullable() {
104 LayoutChildType::Auxiliary("validity".into())
105 } else {
106 LayoutChildType::Field(
107 layout
108 .struct_fields()
109 .field_name(schema_index)
110 .vortex_expect("Field index out of bounds")
111 .clone(),
112 )
113 }
114 }
115
116 fn new_reader(
117 layout: &Self::Layout,
118 name: Arc<str>,
119 segment_source: Arc<dyn SegmentSource>,
120 session: &VortexSession,
121 ctx: &crate::LayoutReaderContext,
122 ) -> VortexResult<LayoutReaderRef> {
123 Ok(Arc::new(StructReader::try_new(
124 layout.clone(),
125 name,
126 segment_source,
127 session.session(),
128 ctx.clone(),
129 )?))
130 }
131
132 fn build(
133 _encoding: &Self::Encoding,
134 dtype: &DType,
135 row_count: u64,
136 _metadata: &<Self::Metadata as DeserializeMetadata>::Output,
137 _segment_ids: Vec<SegmentId>,
138 children: &dyn LayoutChildren,
139 _build_ctx: &LayoutBuildContext<'_>,
140 ) -> VortexResult<Self::Layout> {
141 let struct_dt = dtype
142 .as_struct_fields_opt()
143 .ok_or_else(|| vortex_err!("Expected struct dtype"))?;
144
145 let expected_children = struct_dt.nfields() + (dtype.is_nullable() as usize);
146 vortex_ensure!(
147 children.nchildren() == expected_children,
148 "Struct layout has {} children, but dtype has {} fields",
149 children.nchildren(),
150 struct_dt.nfields()
151 );
152
153 Ok(StructLayout {
154 row_count,
155 dtype: dtype.clone(),
156 children: children.to_arc(),
157 })
158 }
159
160 fn with_children(layout: &mut Self::Layout, children: Vec<LayoutRef>) -> VortexResult<()> {
161 let struct_dt = layout
162 .dtype
163 .as_struct_fields_opt()
164 .ok_or_else(|| vortex_err!("Expected struct dtype"))?;
165
166 let expected_children = struct_dt.nfields() + (layout.dtype.is_nullable() as usize);
167 vortex_ensure!(
168 children.len() == expected_children,
169 "StructLayout expects {} children, got {}",
170 expected_children,
171 children.len()
172 );
173
174 layout.children = OwnedLayoutChildren::layout_children(children);
175 Ok(())
176 }
177}
178
179#[derive(Debug)]
180pub struct StructLayoutEncoding;
181
182#[derive(Clone, Debug)]
186pub struct StructLayout {
187 row_count: u64,
188 dtype: DType,
189 children: Arc<dyn LayoutChildren>,
190}
191
192impl StructLayout {
193 pub fn new(row_count: u64, dtype: DType, children: Vec<LayoutRef>) -> Self {
194 Self {
195 row_count,
196 dtype,
197 children: OwnedLayoutChildren::layout_children(children),
198 }
199 }
200
201 pub fn struct_fields(&self) -> &StructFields {
202 self.dtype
203 .as_struct_fields_opt()
204 .vortex_expect("Struct layout dtype must be a struct")
205 }
206
207 #[inline]
208 pub fn row_count(&self) -> u64 {
209 self.row_count
210 }
211
212 #[inline]
213 pub fn children(&self) -> &Arc<dyn LayoutChildren> {
214 &self.children
215 }
216
217 pub fn matching_fields<F>(&self, field_mask: &[FieldMask], mut per_child: F) -> VortexResult<()>
218 where
219 F: FnMut(FieldMask, usize) -> VortexResult<()>,
220 {
221 if field_mask.iter().any(|mask| mask.matches_all()) {
223 for idx in 0..self.struct_fields().nfields() {
224 per_child(FieldMask::All, idx)?;
225 }
226 return Ok(());
227 }
228
229 for path in field_mask {
231 let Some(field) = path.starting_field()? else {
232 continue;
234 };
235 let Field::Name(field_name) = field else {
236 vortex_bail!("Expected field name, got {field:?}");
237 };
238 let idx = self
239 .struct_fields()
240 .find(field_name)
241 .ok_or_else(|| vortex_err!("Field not found: {field_name}"))?;
242
243 per_child(path.clone().step_into()?, idx)?;
244 }
245
246 Ok(())
247 }
248}