1use std::sync::Arc;
5
6use arrow_array::RecordBatch;
7use arrow_array::RecordBatchReader;
8use arrow_array::cast::AsArray;
9use arrow_schema::ArrowError;
10use arrow_schema::Field;
11use arrow_schema::SchemaRef;
12use futures::Stream;
13use futures::TryStreamExt;
14use vortex_array::ArrayRef;
15use vortex_array::ExecutionCtx;
16use vortex_array::VortexSessionExecute;
17use vortex_arrow::ArrowSessionExt;
18use vortex_error::VortexResult;
19use vortex_io::runtime::BlockingRuntime;
20
21use crate::scan::scan_builder::ScanBuilder;
22
23impl ScanBuilder<ArrayRef> {
24 pub fn into_record_batch_reader<B: BlockingRuntime>(
30 self,
31 schema: SchemaRef,
32 runtime: &B,
33 ) -> VortexResult<impl RecordBatchReader + 'static> {
34 let struct_field = Field::new_struct("", schema.fields().clone(), false);
35 let session = self.session().clone();
36
37 let iter = self
38 .map(move |chunk| {
39 let mut ctx = session.create_execution_ctx();
40 to_record_batch(chunk, &struct_field, &mut ctx)
41 })
42 .into_iter(runtime)?
43 .map(|result| result.map_err(|e| ArrowError::ExternalError(Box::new(e))));
44
45 Ok(RecordBatchIteratorAdapter { iter, schema })
46 }
47
48 pub fn into_record_batch_stream(
49 self,
50 schema: SchemaRef,
51 ) -> VortexResult<impl Stream<Item = Result<RecordBatch, ArrowError>> + Send + 'static> {
52 let struct_field = Field::new_struct("", schema.fields().clone(), false);
53 let session = self.session().clone();
54
55 let stream = self
56 .map(move |chunk| {
57 let mut ctx = session.create_execution_ctx();
58 to_record_batch(chunk, &struct_field, &mut ctx)
59 })
60 .into_stream()?
61 .map_err(|e| ArrowError::ExternalError(Box::new(e)));
62
63 Ok(stream)
64 }
65}
66
67fn to_record_batch(
68 chunk: ArrayRef,
69 data_type: &Field,
70 ctx: &mut ExecutionCtx,
71) -> VortexResult<RecordBatch> {
72 let session = ctx.session().clone();
73 let arrow = session.arrow().execute_arrow(chunk, Some(data_type), ctx)?;
74 Ok(RecordBatch::from(arrow.as_struct().clone()))
75}
76
77#[derive(Clone)]
80pub struct RecordBatchIteratorAdapter<I> {
81 iter: I,
82 schema: SchemaRef,
83}
84
85impl<I> RecordBatchIteratorAdapter<I> {
86 pub fn new(iter: I, schema: SchemaRef) -> Self {
88 Self { iter, schema }
89 }
90}
91
92impl<I> Iterator for RecordBatchIteratorAdapter<I>
93where
94 I: Iterator<Item = Result<RecordBatch, ArrowError>>,
95{
96 type Item = Result<RecordBatch, ArrowError>;
97
98 #[inline]
99 fn next(&mut self) -> Option<Self::Item> {
100 self.iter.next()
101 }
102}
103
104impl<I> RecordBatchReader for RecordBatchIteratorAdapter<I>
105where
106 I: Iterator<Item = Result<RecordBatch, ArrowError>>,
107{
108 #[inline]
109 fn schema(&self) -> SchemaRef {
110 Arc::clone(&self.schema)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use std::sync::Arc;
117
118 use arrow_array::Array;
119 use arrow_array::ArrayRef as ArrowArrayRef;
120 use arrow_array::Int32Array;
121 use arrow_array::RecordBatch;
122 use arrow_array::StringArray;
123 use arrow_array::StructArray;
124 use arrow_array::cast::AsArray;
125 use arrow_schema::ArrowError;
126 use arrow_schema::DataType;
127 use arrow_schema::Field;
128 use arrow_schema::Schema;
129 use vortex_array::ArrayRef;
130 use vortex_array::VortexSessionExecute;
131 use vortex_arrow::ArrowSessionExt;
132 use vortex_error::VortexResult;
133
134 use super::*;
135 use crate::scan::test::SCAN_SESSION;
136
137 fn create_test_struct_array() -> VortexResult<ArrayRef> {
138 let id_array = Int32Array::from(vec![Some(1), Some(2), None, Some(4)]);
140 let name_array = StringArray::from(vec![Some("Alice"), Some("Bob"), Some("Charlie"), None]);
141
142 let schema = Arc::new(Schema::new(vec![
144 Field::new("id", DataType::Int32, true),
145 Field::new("name", DataType::Utf8, true),
146 ]));
147
148 let struct_array = StructArray::new(
149 schema.fields().clone(),
150 vec![
151 Arc::new(id_array) as ArrowArrayRef,
152 Arc::new(name_array) as ArrowArrayRef,
153 ],
154 None,
155 );
156
157 SCAN_SESSION
159 .arrow()
160 .from_arrow_array(Arc::new(struct_array), true)
161 }
162
163 fn create_arrow_schema() -> Arc<Schema> {
164 Arc::new(Schema::new(vec![
165 Field::new("id", DataType::Int32, true),
166 Field::new("name", DataType::Utf8, true),
167 ]))
168 }
169
170 #[test]
171 fn test_record_batch_conversion() -> VortexResult<()> {
172 let vortex_array = create_test_struct_array()?;
173 let schema = create_arrow_schema();
174 let struct_field = Field::new_struct("", schema.fields().clone(), false);
175 let mut ctx = SCAN_SESSION.create_execution_ctx();
176
177 let batch = to_record_batch(vortex_array, &struct_field, &mut ctx)?;
178 assert_eq!(batch.num_columns(), 2);
179 assert_eq!(batch.num_rows(), 4);
180
181 let id_col = batch
183 .column(0)
184 .as_primitive::<arrow_array::types::Int32Type>();
185 assert_eq!(id_col.value(0), 1);
186 assert_eq!(id_col.value(1), 2);
187 assert!(id_col.is_null(2));
188 assert_eq!(id_col.value(3), 4);
189
190 let name_col = batch.column(1).as_string::<i32>();
192 assert_eq!(name_col.value(0), "Alice");
193 assert_eq!(name_col.value(1), "Bob");
194 assert_eq!(name_col.value(2), "Charlie");
195 assert!(name_col.is_null(3));
196
197 Ok(())
198 }
199
200 #[test]
201 fn test_record_batch_iterator_adapter() -> VortexResult<()> {
202 let schema = create_arrow_schema();
203 let batch1 = RecordBatch::try_new(
204 Arc::clone(&schema),
205 vec![
206 Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrowArrayRef,
207 Arc::new(StringArray::from(vec![Some("Alice"), Some("Bob")])) as ArrowArrayRef,
208 ],
209 )?;
210 let batch2 = RecordBatch::try_new(
211 Arc::clone(&schema),
212 vec![
213 Arc::new(Int32Array::from(vec![None, Some(4)])) as ArrowArrayRef,
214 Arc::new(StringArray::from(vec![Some("Charlie"), None])) as ArrowArrayRef,
215 ],
216 )?;
217
218 let iter = vec![Ok(batch1), Ok(batch2)].into_iter();
219 let mut adapter = RecordBatchIteratorAdapter {
220 iter,
221 schema: Arc::clone(&schema),
222 };
223
224 assert_eq!(adapter.schema(), schema);
226
227 let first = adapter.next().unwrap()?;
229 assert_eq!(first.num_rows(), 2);
230
231 let second = adapter.next().unwrap()?;
232 assert_eq!(second.num_rows(), 2);
233
234 assert!(adapter.next().is_none());
235
236 Ok(())
237 }
238
239 #[test]
240 fn test_error_in_iterator() {
241 let schema = create_arrow_schema();
242 let error = ArrowError::ComputeError("test error".to_string());
243
244 let iter = vec![Err(error)].into_iter();
245 let mut adapter = RecordBatchIteratorAdapter {
246 iter,
247 schema: Arc::clone(&schema),
248 };
249
250 assert_eq!(adapter.schema(), schema);
252 let result = adapter.next().unwrap();
253 assert!(result.is_err());
254 }
255
256 #[test]
257 fn test_mixed_success_and_error() {
258 let schema = create_arrow_schema();
259 let batch = RecordBatch::try_new(
260 Arc::clone(&schema),
261 vec![
262 Arc::new(Int32Array::from(vec![Some(1)])) as ArrowArrayRef,
263 Arc::new(StringArray::from(vec![Some("Test")])) as ArrowArrayRef,
264 ],
265 )
266 .unwrap();
267
268 let error = ArrowError::ComputeError("test error".to_string());
269
270 let iter = vec![Ok(batch.clone()), Err(error), Ok(batch)].into_iter();
271 let mut adapter = RecordBatchIteratorAdapter { iter, schema };
272
273 let first = adapter.next().unwrap();
275 assert!(first.is_ok());
276
277 let second = adapter.next().unwrap();
279 assert!(second.is_err());
280
281 let third = adapter.next().unwrap();
283 assert!(third.is_ok());
284 }
285}