common_recordbatch/
cursor.rs1use datatypes::schema::SchemaRef;
16use futures::StreamExt;
17use tokio::sync::Mutex;
18
19use crate::error::Result;
20use crate::recordbatch::merge_record_batches;
21use crate::{RecordBatch, SendableRecordBatchStream};
22
23struct Inner {
24 stream: SendableRecordBatchStream,
25 current_row_index: usize,
26 current_batch: Option<RecordBatch>,
27 total_rows_in_current_batch: usize,
28}
29
30pub struct RecordBatchStreamCursor {
32 schema: SchemaRef,
33 inner: Mutex<Inner>,
34}
35
36impl RecordBatchStreamCursor {
37 pub fn new(stream: SendableRecordBatchStream) -> RecordBatchStreamCursor {
38 let schema = stream.schema();
39 Self {
40 schema,
41 inner: Mutex::new(Inner {
42 stream,
43 current_row_index: 0,
44 current_batch: None,
45 total_rows_in_current_batch: 0,
46 }),
47 }
48 }
49
50 pub fn schema(&self) -> SchemaRef {
51 self.schema.clone()
52 }
53
54 pub async fn take(&self, size: usize) -> Result<RecordBatch> {
57 let mut remaining_rows_to_take = size;
58 let mut accumulated_rows = Vec::new();
59
60 let mut inner = self.inner.lock().await;
61
62 while remaining_rows_to_take > 0 {
63 if inner.current_batch.is_none()
65 || inner.current_row_index >= inner.total_rows_in_current_batch
66 {
67 match inner.stream.next().await {
68 Some(Ok(batch)) => {
69 inner.total_rows_in_current_batch = batch.num_rows();
70 inner.current_batch = Some(batch);
71 inner.current_row_index = 0;
72 }
73 Some(Err(e)) => return Err(e),
74 None => {
75 break;
77 }
78 }
79 }
80
81 let current_batch = match &inner.current_batch {
83 Some(batch) => batch,
84 None => break,
85 };
86
87 let rows_to_take_from_batch = remaining_rows_to_take
89 .min(inner.total_rows_in_current_batch - inner.current_row_index);
90
91 let taken_batch =
93 current_batch.slice(inner.current_row_index, rows_to_take_from_batch)?;
94
95 accumulated_rows.push(taken_batch);
97
98 inner.current_row_index += rows_to_take_from_batch;
100 remaining_rows_to_take -= rows_to_take_from_batch;
101 }
102
103 if accumulated_rows.is_empty() {
105 return Ok(RecordBatch::new_empty(inner.stream.schema()));
106 }
107
108 if accumulated_rows.len() == 1 {
110 return Ok(accumulated_rows.remove(0));
111 }
112
113 merge_record_batches(inner.stream.schema(), &accumulated_rows)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use std::sync::Arc;
121
122 use datatypes::prelude::ConcreteDataType;
123 use datatypes::schema::{ColumnSchema, Schema};
124 use datatypes::vectors::StringVector;
125
126 use super::*;
127 use crate::RecordBatches;
128
129 #[tokio::test]
130 async fn test_cursor() {
131 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
132 "a",
133 ConcreteDataType::string_datatype(),
134 false,
135 )]));
136
137 let rbs = RecordBatches::try_from_columns(
138 schema.clone(),
139 vec![Arc::new(StringVector::from(vec!["hello", "world"])) as _],
140 )
141 .unwrap();
142
143 let cursor = RecordBatchStreamCursor::new(rbs.as_stream());
144 let result_rb = cursor.take(1).await.expect("take from cursor failed");
145 assert_eq!(result_rb.num_rows(), 1);
146
147 let result_rb = cursor.take(1).await.expect("take from cursor failed");
148 assert_eq!(result_rb.num_rows(), 1);
149
150 let result_rb = cursor.take(1).await.expect("take from cursor failed");
151 assert_eq!(result_rb.num_rows(), 0);
152
153 let rb = RecordBatch::new(
154 schema.clone(),
155 vec![Arc::new(StringVector::from(vec!["hello", "world"])) as _],
156 )
157 .unwrap();
158 let rbs2 =
159 RecordBatches::try_new(schema.clone(), vec![rb.clone(), rb.clone(), rb]).unwrap();
160 let cursor = RecordBatchStreamCursor::new(rbs2.as_stream());
161 let result_rb = cursor.take(3).await.expect("take from cursor failed");
162 assert_eq!(result_rb.num_rows(), 3);
163 let result_rb = cursor.take(2).await.expect("take from cursor failed");
164 assert_eq!(result_rb.num_rows(), 2);
165 let result_rb = cursor.take(2).await.expect("take from cursor failed");
166 assert_eq!(result_rb.num_rows(), 1);
167 let result_rb = cursor.take(2).await.expect("take from cursor failed");
168 assert_eq!(result_rb.num_rows(), 0);
169
170 let rb = RecordBatch::new(
171 schema.clone(),
172 vec![Arc::new(StringVector::from(vec!["hello", "world"])) as _],
173 )
174 .unwrap();
175 let rbs3 =
176 RecordBatches::try_new(schema.clone(), vec![rb.clone(), rb.clone(), rb]).unwrap();
177 let cursor = RecordBatchStreamCursor::new(rbs3.as_stream());
178 let result_rb = cursor.take(10).await.expect("take from cursor failed");
179 assert_eq!(result_rb.num_rows(), 6);
180 }
181}