harmos_storage/query/
table.rs1use std::sync::Arc;
20
21use arrow_array::RecordBatch;
23use arrow_schema::Schema;
24
25use super::spill::{Builder, Pages, Spool};
27use super::transform::{Transform, TransformExecution, invalid};
28use crate::Result;
29
30#[derive(Clone)]
40pub struct Table {
41 spool: Spool,
42}
43
44pub struct TableWriter {
46 builder: Builder,
47 schema: Arc<Schema>,
48}
49
50pub struct TableReader {
52 pages: Pages,
53}
54
55impl TableWriter {
62 pub fn new(schema: Arc<Schema>) -> Result<Self> {
63 crate::storage::arrow::validate_schema(&schema)?;
64 let builder = Builder::new(schema.clone())?;
65 Ok(Self { builder, schema })
66 }
67
68 pub fn push(&mut self, page: &RecordBatch, check: &mut impl FnMut() -> Result<()>) -> Result<()> {
69 check()?;
70 if page.schema() != self.schema {
71 return Err(invalid("derived table schema changed"));
72 }
73 self.builder.push(page, check)
74 }
75
76 pub fn finish(self) -> Result<Table> {
77 Ok(Table { spool: self.builder.finish()? })
78 }
79}
80
81impl Table {
82 pub fn len(&self) -> usize {
83 self.spool.rows
84 }
85
86 pub fn is_empty(&self) -> bool {
87 self.len() == 0
88 }
89
90 pub fn schema(&self) -> Arc<Schema> {
91 self.spool.schema.clone()
92 }
93
94 pub fn transform(&self, transform: &Transform, check: &mut impl FnMut() -> Result<()>) -> Result<Self> {
96 check()?;
97 let schema = transform.output_schema(&self.spool.schema)?;
98 crate::storage::arrow::validate_schema(&schema)?;
99 let spool = super::reduction::transform(self.spool.clone(), transform, check)?;
100 Ok(Self { spool })
101 }
102
103 pub fn reader(&self) -> Result<TableReader> {
104 Ok(TableReader { pages: self.spool.clone().pages()? })
105 }
106}
107
108impl TableReader {
109 pub fn next(&mut self, check: &mut impl FnMut() -> Result<()>) -> Result<Option<RecordBatch>> {
110 check()?;
111 self.pages.next()
112 }
113}
114
115#[cfg(test)]
122mod tests {
123 use super::super::transform::{
124 FieldPath,
125 downsample::{Algorithm, Downsample},
126 };
127 use super::*;
128 use arrow_array::Float64Array;
129 use arrow_schema::{DataType, Field};
130
131 #[test]
132 fn derived_table_reduces_exactly_across_pages_and_survives_owner_drop() {
133 let schema = Arc::new(Schema::new(vec![Field::new("x", DataType::Float64, false), Field::new("y", DataType::Float64, false)]));
134 let mut writer = TableWriter::new(schema.clone()).unwrap();
135 let x: Vec<f64> = (0..10_080).map(f64::from).collect();
136 let y: Vec<f64> = x.iter().map(|x| (x * 0.13).sin()).collect();
137 for (x, y) in x.chunks(120).zip(y.chunks(120)) {
138 let page = RecordBatch::try_new(
139 schema.clone(),
140 vec![Arc::new(Float64Array::from(x.to_vec())), Arc::new(Float64Array::from(y.to_vec()))],
141 )
142 .unwrap();
143 writer.push(&page, &mut || Ok(())).unwrap();
144 }
145 let table = writer.finish().unwrap();
146 assert_eq!(table.len(), x.len());
147 for (algorithm, expected) in [
148 (Algorithm::Lttb, crate::signal::lttb(&x, &y, 31)),
149 (Algorithm::Extrema, crate::signal::extrema(&y, 15)),
150 (Algorithm::Decimate, crate::signal::decimate(x.len(), 31)),
151 ] {
152 let transform = Transform::Downsample(Downsample {
153 max_rows: 31,
154 algorithm,
155 value: Some(FieldPath { fields: vec!["y".into()] }),
156 axis: Some(FieldPath { fields: vec!["x".into()] }),
157 });
158 let reduced = table.transform(&transform, &mut || Ok(())).unwrap();
159 let mut reader = reduced.reader().unwrap();
160 drop(reduced);
161 let mut actual = Vec::new();
162 while let Some(page) = reader.next(&mut || Ok(())).unwrap() {
163 let values = page.column(0).as_any().downcast_ref::<Float64Array>().unwrap();
164 actual.extend(values.values().iter().map(|value| *value as usize));
165 }
166 assert_eq!(actual, expected);
167 }
168 let mut reader = table.reader().unwrap();
169 drop(table);
170 let mut rows = 0;
171 while let Some(page) = reader.next(&mut || Ok(())).unwrap() {
172 assert!(page.num_rows() <= super::super::spill::PAGE_ROWS);
173 rows += page.num_rows();
174 }
175 assert_eq!(rows, x.len());
176 }
177
178 #[test]
179 fn derived_table_rejects_changed_schema_and_polls_cancellation() {
180 let schema = Arc::new(Schema::new(vec![Field::new("x", DataType::Float64, false)]));
181 let mut writer = TableWriter::new(schema.clone()).unwrap();
182 let wrong = RecordBatch::new_empty(Arc::new(Schema::empty()));
183 assert!(writer.push(&wrong, &mut || Ok(())).is_err());
184 let page = RecordBatch::new_empty(schema);
185 assert!(writer.push(&page, &mut || Err(invalid("cancelled"))).is_err());
186 let table = writer.finish().unwrap();
187 assert!(table.is_empty());
188 let mut reader = table.reader().unwrap();
189 assert!(reader.next(&mut || Err(invalid("cancelled"))).is_err());
190 assert!(reader.next(&mut || Ok(())).unwrap().is_none());
191 }
192}