1use crate::batch::{Batch, PhysicalRow, RowSchema, DEFAULT_BATCH_SIZE};
17use crate::physical::{ExecResult, PhysicalOperator, PhysicalOrder};
18use uqa_sql::ast::ColumnType;
19use uqa_sql::ResultRow;
20
21pub trait RowSource: Send {
26 fn schema(&self) -> &[String];
28
29 fn physical_schema(&self) -> Option<&RowSchema> {
33 None
34 }
35
36 fn estimated_cardinality(&self) -> Option<u64> {
38 None
39 }
40
41 fn output_ordering(&self) -> &[PhysicalOrder] {
43 &[]
44 }
45
46 fn next_row(&mut self) -> ExecResult<Option<ResultRow>>;
48
49 fn next_batch(&mut self, max_rows: usize) -> ExecResult<Vec<ResultRow>> {
53 let mut rows = Vec::with_capacity(max_rows);
54 while rows.len() < max_rows {
55 match self.next_row()? {
56 Some(row) => rows.push(row),
57 None => break,
58 }
59 }
60 Ok(rows)
61 }
62
63 fn next_physical_batch(&mut self, max_rows: usize) -> ExecResult<Vec<PhysicalRow>> {
67 let schema = RowSchema::new(self.schema().to_vec());
68 self.next_batch(max_rows).map(|rows| {
69 rows.into_iter()
70 .map(|row| PhysicalRow::from_result_row(&schema, row))
71 .collect()
72 })
73 }
74
75 fn consume_into_aggregate(
79 &mut self,
80 _executor: &mut dyn crate::relational::AggregateExecutor,
81 ) -> ExecResult<bool> {
82 Ok(false)
83 }
84}
85
86pub struct VecSource {
89 schema: Vec<String>,
90 physical_schema: RowSchema,
91 rows: std::vec::IntoIter<ResultRow>,
92}
93
94pub struct PhysicalVecSource {
96 schema: RowSchema,
97 rows: std::vec::IntoIter<PhysicalRow>,
98}
99
100pub struct RowIteratorScan<'a> {
104 schema: RowSchema,
105 rows: Box<dyn Iterator<Item = ExecResult<ResultRow>> + Send + 'a>,
106 exhausted: bool,
107}
108
109impl<'a> RowIteratorScan<'a> {
110 pub fn new(
111 schema: Vec<String>,
112 rows: Box<dyn Iterator<Item = ExecResult<ResultRow>> + Send + 'a>,
113 ) -> Self {
114 Self {
115 schema: RowSchema::new(schema),
116 rows,
117 exhausted: false,
118 }
119 }
120
121 pub fn with_types(
122 schema: Vec<String>,
123 types: Vec<Option<ColumnType>>,
124 rows: Box<dyn Iterator<Item = ExecResult<ResultRow>> + Send + 'a>,
125 ) -> Self {
126 Self {
127 schema: RowSchema::with_types(schema, types),
128 rows,
129 exhausted: false,
130 }
131 }
132
133 pub fn with_row_schema(
134 schema: RowSchema,
135 rows: Box<dyn Iterator<Item = ExecResult<ResultRow>> + Send + 'a>,
136 ) -> Self {
137 Self {
138 schema,
139 rows,
140 exhausted: false,
141 }
142 }
143}
144
145impl PhysicalOperator for RowIteratorScan<'_> {
146 fn row_schema(&self) -> &RowSchema {
147 &self.schema
148 }
149
150 fn open(&mut self) -> ExecResult<()> {
151 self.exhausted = false;
152 Ok(())
153 }
154
155 fn next(&mut self) -> ExecResult<Option<Batch>> {
156 if self.exhausted {
157 return Ok(None);
158 }
159 let mut batch = Vec::with_capacity(DEFAULT_BATCH_SIZE);
160 while batch.len() < DEFAULT_BATCH_SIZE {
161 match self.rows.next() {
162 Some(Ok(row)) => batch.push(row),
163 Some(Err(error)) => return Err(error),
164 None => {
165 self.exhausted = true;
166 break;
167 }
168 }
169 }
170 if batch.is_empty() {
171 Ok(None)
172 } else {
173 Ok(Some(Batch::new(self.schema.clone(), batch)))
174 }
175 }
176
177 fn close(&mut self) -> ExecResult<()> {
178 self.exhausted = true;
179 Ok(())
180 }
181}
182
183impl VecSource {
184 pub fn new(schema: Vec<String>, rows: Vec<ResultRow>) -> Self {
185 let physical_schema = RowSchema::from_named_columns(schema.clone());
186 Self {
187 schema,
188 physical_schema,
189 rows: rows.into_iter(),
190 }
191 }
192
193 pub fn with_row_schema(physical_schema: RowSchema, rows: Vec<ResultRow>) -> Self {
194 Self {
195 schema: physical_schema.columns().to_vec(),
196 physical_schema,
197 rows: rows.into_iter(),
198 }
199 }
200
201 pub fn with_types(
202 schema: Vec<String>,
203 types: Vec<Option<ColumnType>>,
204 rows: Vec<ResultRow>,
205 ) -> Self {
206 let physical_schema = RowSchema::with_types(schema.clone(), types);
207 Self {
208 schema,
209 physical_schema,
210 rows: rows.into_iter(),
211 }
212 }
213}
214
215impl RowSource for VecSource {
216 fn schema(&self) -> &[String] {
217 &self.schema
218 }
219
220 fn physical_schema(&self) -> Option<&RowSchema> {
221 Some(&self.physical_schema)
222 }
223
224 fn estimated_cardinality(&self) -> Option<u64> {
225 u64::try_from(self.rows.len()).ok()
226 }
227
228 fn next_row(&mut self) -> ExecResult<Option<ResultRow>> {
229 Ok(self.rows.next())
230 }
231}
232
233impl PhysicalVecSource {
234 pub fn new(schema: RowSchema, rows: Vec<PhysicalRow>) -> Self {
235 Self {
236 schema,
237 rows: rows.into_iter(),
238 }
239 }
240}
241
242impl RowSource for PhysicalVecSource {
243 fn schema(&self) -> &[String] {
244 self.schema.columns()
245 }
246
247 fn physical_schema(&self) -> Option<&RowSchema> {
248 Some(&self.schema)
249 }
250
251 fn estimated_cardinality(&self) -> Option<u64> {
252 u64::try_from(self.rows.len()).ok()
253 }
254
255 fn next_row(&mut self) -> ExecResult<Option<ResultRow>> {
256 Ok(self
257 .rows
258 .next()
259 .map(|row| self.schema.view(&row).to_result_row()))
260 }
261
262 fn next_physical_batch(&mut self, max_rows: usize) -> ExecResult<Vec<PhysicalRow>> {
263 Ok(self.rows.by_ref().take(max_rows).collect())
264 }
265}
266
267pub struct TableScan {
272 source: Option<Box<dyn RowSource>>,
273 schema: RowSchema,
274 ordering: Vec<PhysicalOrder>,
275 estimated_cardinality: Option<u64>,
276 exhausted: bool,
277}
278
279impl TableScan {
280 pub fn new(source: Box<dyn RowSource>) -> Self {
281 let schema = source
282 .physical_schema()
283 .cloned()
284 .unwrap_or_else(|| RowSchema::new(source.schema().to_vec()));
285 let ordering = source.output_ordering().to_vec();
286 let estimated_cardinality = source.estimated_cardinality();
287 Self {
288 source: Some(source),
289 schema,
290 ordering,
291 estimated_cardinality,
292 exhausted: false,
293 }
294 }
295
296 pub fn from_rows(schema: Vec<String>, rows: Vec<ResultRow>) -> Self {
297 Self::new(Box::new(VecSource::new(schema, rows)))
298 }
299
300 pub fn from_typed_rows(
301 schema: Vec<String>,
302 types: Vec<Option<ColumnType>>,
303 rows: Vec<ResultRow>,
304 ) -> Self {
305 Self::new(Box::new(VecSource::with_types(schema, types, rows)))
306 }
307
308 pub fn from_rows_with_schema(schema: RowSchema, rows: Vec<ResultRow>) -> Self {
309 Self::new(Box::new(VecSource::with_row_schema(schema, rows)))
310 }
311
312 pub fn from_physical_rows(schema: RowSchema, rows: Vec<PhysicalRow>) -> Self {
313 Self::new(Box::new(PhysicalVecSource::new(schema, rows)))
314 }
315}
316
317impl PhysicalOperator for TableScan {
318 fn row_schema(&self) -> &RowSchema {
319 &self.schema
320 }
321
322 fn estimated_cardinality(&self) -> Option<u64> {
323 self.estimated_cardinality
324 }
325
326 fn output_ordering(&self) -> &[PhysicalOrder] {
327 &self.ordering
328 }
329
330 fn consume_into_aggregate(
331 &mut self,
332 executor: &mut dyn crate::relational::AggregateExecutor,
333 ) -> ExecResult<bool> {
334 let Some(source) = self.source.as_mut() else {
335 return Ok(false);
336 };
337 source.consume_into_aggregate(executor)
338 }
339
340 fn open(&mut self) -> ExecResult<()> {
341 self.exhausted = false;
342 Ok(())
343 }
344
345 fn next(&mut self) -> ExecResult<Option<Batch>> {
346 if self.exhausted {
347 return Ok(None);
348 }
349 let Some(src) = self.source.as_mut() else {
350 return Ok(None);
351 };
352 let buf = src.next_physical_batch(DEFAULT_BATCH_SIZE)?;
353 if buf.is_empty() {
354 self.exhausted = true;
355 return Ok(None);
356 }
357 Ok(Some(Batch::from_physical_rows(self.schema.clone(), buf)))
358 }
359
360 fn close(&mut self) -> ExecResult<()> {
361 self.source = None;
362 self.exhausted = true;
363 Ok(())
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370 use crate::physical::run_to_rows;
371 use uqa_core::Value;
372
373 fn row<const N: usize>(pairs: [(&str, Value); N]) -> ResultRow {
374 pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect()
375 }
376
377 #[test]
378 fn table_scan_drains_source() {
379 let rows = vec![
380 row([("id", Value::Int(1)), ("name", Value::Str("a".into()))]),
381 row([("id", Value::Int(2)), ("name", Value::Str("b".into()))]),
382 ];
383 let mut scan = TableScan::from_rows(vec!["id".into(), "name".into()], rows);
384 let (cols, rows) = run_to_rows(&mut scan).unwrap();
385 assert_eq!(cols, vec!["id", "name"]);
386 assert_eq!(rows.len(), 2);
387 }
388
389 #[test]
390 fn table_scan_empty_source_returns_no_batch() {
391 let mut scan = TableScan::from_rows(vec!["id".into()], Vec::new());
392 let (_cols, rows) = run_to_rows(&mut scan).unwrap();
393 assert!(rows.is_empty());
394 }
395
396 #[test]
397 fn iterator_scan_propagates_a_late_producer_error() {
398 let rows = vec![
399 Ok(row([("id", Value::Int(1))])),
400 Err(crate::ExecError::Other("late producer failure".into())),
401 ];
402 let mut scan = RowIteratorScan::new(vec!["id".into()], Box::new(rows.into_iter()));
403 let error = run_to_rows(&mut scan).unwrap_err();
404 assert!(error.to_string().contains("late producer failure"));
405 }
406}