1use std::ops::Range;
10use std::sync::Arc;
11
12use itertools::Itertools;
13use vortex_array::ArrayRef;
14use vortex_array::Columnar;
15use vortex_array::IntoArray;
16use vortex_array::VortexSessionExecute;
17use vortex_array::arrays::ConstantArray;
18use vortex_array::dtype::DType;
19use vortex_array::dtype::Field;
20use vortex_array::dtype::FieldMask;
21use vortex_array::dtype::FieldPath;
22use vortex_array::dtype::FieldPathSet;
23use vortex_array::expr::Expression;
24use vortex_array::expr::pruning::checked_pruning_expr;
25use vortex_array::scalar_fn::internal::row_count::substitute_row_count;
26use vortex_error::VortexResult;
27use vortex_layout::LayoutReader;
28use vortex_layout::scan::layout::LayoutReaderDataSource;
29use vortex_layout::scan::scan_builder::ScanBuilder;
30use vortex_layout::scan::split_by::SplitBy;
31use vortex_layout::segments::SegmentSource;
32use vortex_scan::DataSourceRef;
33use vortex_session::VortexSession;
34use vortex_utils::aliases::hash_map::HashMap;
35
36use crate::FileStatistics;
37use crate::footer::Footer;
38use crate::pruning::extract_relevant_file_stats_as_struct_row;
39use crate::v2::FileStatsLayoutReader;
40
41#[derive(Clone)]
47pub struct VortexFile {
48 footer: Footer,
50 segment_source: Arc<dyn SegmentSource>,
52 session: VortexSession,
54}
55
56impl VortexFile {
57 pub fn new(
59 footer: Footer,
60 segment_source: Arc<dyn SegmentSource>,
61 session: VortexSession,
62 ) -> Self {
63 Self {
64 footer,
65 segment_source,
66 session,
67 }
68 }
69
70 pub fn footer(&self) -> &Footer {
72 &self.footer
73 }
74
75 pub fn row_count(&self) -> u64 {
77 self.footer.row_count()
78 }
79
80 pub fn dtype(&self) -> &DType {
82 self.footer.dtype()
83 }
84
85 pub fn file_stats(&self) -> Option<&FileStatistics> {
89 self.footer.statistics()
90 }
91
92 pub fn segment_source(&self) -> Arc<dyn SegmentSource> {
97 Arc::clone(&self.segment_source)
98 }
99
100 pub fn session(&self) -> &VortexSession {
102 &self.session
103 }
104
105 pub fn layout_reader(&self) -> VortexResult<Arc<dyn LayoutReader>> {
109 let segment_source = self.segment_source();
110
111 let root_reader = self
112 .footer
113 .layout()
114 .new_reader("".into(), segment_source, &self.session)?;
116
117 Ok(if let Some(stats) = self.file_stats().cloned() {
118 Arc::new(FileStatsLayoutReader::new(
119 root_reader,
120 stats,
121 self.session.clone(),
122 ))
123 } else {
124 root_reader
125 })
126 }
127
128 pub fn data_source(&self) -> VortexResult<DataSourceRef> {
133 let reader = self.layout_reader()?;
134
135 Ok(Arc::new(LayoutReaderDataSource::new(
136 reader,
137 self.session.clone(),
138 )))
139 }
140
141 pub fn scan(&self) -> VortexResult<ScanBuilder<ArrayRef>> {
143 Ok(ScanBuilder::new(
144 self.session.clone(),
145 self.layout_reader()?,
146 ))
147 }
148
149 pub fn can_prune(&self, filter: &Expression) -> VortexResult<bool> {
155 let Some((stats, fields)) = self
156 .footer
157 .statistics()
158 .zip(self.footer.dtype().as_struct_fields_opt())
159 else {
160 return Ok(false);
161 };
162
163 let set = FieldPathSet::from_iter(
164 fields
165 .names()
166 .iter()
167 .zip(stats.stats_sets().iter())
168 .flat_map(|(name, stats)| {
169 stats.iter().map(|(stat, _)| {
170 FieldPath::from_iter([
171 Field::Name(name.clone()),
172 Field::Name(stat.name().into()),
173 ])
174 })
175 }),
176 );
177
178 let Some((predicate, required_stats)) = checked_pruning_expr(filter, &set) else {
179 return Ok(false);
180 };
181
182 let required_file_stats = HashMap::from_iter(
183 required_stats
184 .map()
185 .iter()
186 .map(|(path, stats)| (path.clone(), stats.clone())),
187 );
188
189 let Some(file_stats) = extract_relevant_file_stats_as_struct_row(
190 &required_file_stats,
191 stats.stats_sets(),
192 fields,
193 )?
194 else {
195 return Ok(false);
196 };
197
198 let applied = file_stats.apply(&predicate)?;
201 let row_count_replacement =
202 ConstantArray::new(self.footer.row_count(), applied.len()).into_array();
203 let applied = substitute_row_count(applied, &row_count_replacement)?;
204
205 let mut ctx = self.session.create_execution_ctx();
206 Ok(match applied.execute::<Columnar>(&mut ctx)? {
207 Columnar::Constant(s) => s.scalar().as_bool().value() == Some(true),
208 Columnar::Canonical(_) => false,
209 })
210 }
211
212 pub fn splits(&self) -> VortexResult<Vec<Range<u64>>> {
213 let reader = self.layout_reader()?;
214 Ok(SplitBy::Layout
215 .splits(reader.as_ref(), &(0..reader.row_count()), &[FieldMask::All])?
216 .into_iter()
217 .tuple_windows()
218 .map(|(start, end)| start..end)
219 .collect())
220 }
221}