Skip to main content

common_recordbatch/
util.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::pin::Pin;
16use std::sync::Arc;
17use std::task::{Context, Poll};
18
19use arc_swap::ArcSwapOption;
20use datatypes::schema::SchemaRef;
21use futures::{Stream, StreamExt, TryStreamExt};
22use snafu::ensure;
23
24use crate::adapter::RecordBatchMetrics;
25use crate::error::{EmptyStreamSnafu, Result, SchemaNotMatchSnafu};
26use crate::{
27    OrderOption, RecordBatch, RecordBatchStream, RecordBatches, SendableRecordBatchStream,
28};
29
30/// Collect all the items from the stream into a vector of [`RecordBatch`].
31pub async fn collect(stream: SendableRecordBatchStream) -> Result<Vec<RecordBatch>> {
32    stream.try_collect::<Vec<_>>().await
33}
34
35/// Collect all the items from the stream into [RecordBatches].
36pub async fn collect_batches(stream: SendableRecordBatchStream) -> Result<RecordBatches> {
37    let schema = stream.schema();
38    let batches = stream.try_collect::<Vec<_>>().await?;
39    RecordBatches::try_new(schema, batches)
40}
41
42/// A stream that chains multiple streams into a single stream.
43pub struct ChainedRecordBatchStream {
44    inputs: Vec<SendableRecordBatchStream>,
45    curr_index: usize,
46    schema: SchemaRef,
47    metrics: Arc<ArcSwapOption<RecordBatchMetrics>>,
48}
49
50impl ChainedRecordBatchStream {
51    pub fn new(inputs: Vec<SendableRecordBatchStream>) -> Result<Self> {
52        // check length
53        ensure!(!inputs.is_empty(), EmptyStreamSnafu);
54
55        // check schema
56        let first_schema = inputs[0].schema();
57        for input in inputs.iter().skip(1) {
58            let schema = input.schema();
59            ensure!(
60                first_schema == schema,
61                SchemaNotMatchSnafu {
62                    left: first_schema,
63                    right: schema
64                }
65            );
66        }
67
68        Ok(Self {
69            inputs,
70            curr_index: 0,
71            schema: first_schema,
72            metrics: Default::default(),
73        })
74    }
75
76    fn sequence_poll(
77        mut self: Pin<&mut Self>,
78        ctx: &mut Context<'_>,
79    ) -> Poll<Option<Result<RecordBatch>>> {
80        if self.curr_index >= self.inputs.len() {
81            return Poll::Ready(None);
82        }
83
84        let curr_index = self.curr_index;
85        match self.inputs[curr_index].poll_next_unpin(ctx) {
86            Poll::Ready(Some(Ok(batch))) => Poll::Ready(Some(Ok(batch))),
87            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
88            Poll::Ready(None) => {
89                self.curr_index += 1;
90                if self.curr_index < self.inputs.len() {
91                    self.sequence_poll(ctx)
92                } else {
93                    Poll::Ready(None)
94                }
95            }
96            Poll::Pending => Poll::Pending,
97        }
98    }
99}
100
101/// A stream that stops after yielding at most `remaining` rows.
102pub struct LimitedRecordBatchStream {
103    input: Option<SendableRecordBatchStream>,
104    remaining: usize,
105    schema: SchemaRef,
106    output_ordering: Option<Vec<OrderOption>>,
107}
108
109impl LimitedRecordBatchStream {
110    pub fn new(input: SendableRecordBatchStream, limit: usize) -> Self {
111        let schema = input.schema();
112        let output_ordering = input.output_ordering().map(|o| o.to_vec());
113        Self {
114            input: Some(input),
115            remaining: limit,
116            schema,
117            output_ordering,
118        }
119    }
120}
121
122impl RecordBatchStream for LimitedRecordBatchStream {
123    fn name(&self) -> &str {
124        "LimitedRecordBatchStream"
125    }
126
127    fn schema(&self) -> SchemaRef {
128        self.schema.clone()
129    }
130
131    fn output_ordering(&self) -> Option<&[OrderOption]> {
132        self.output_ordering.as_deref()
133    }
134
135    fn metrics(&self) -> Option<RecordBatchMetrics> {
136        self.input.as_ref().and_then(|input| input.metrics())
137    }
138}
139
140impl Stream for LimitedRecordBatchStream {
141    type Item = Result<RecordBatch>;
142
143    fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
144        if self.remaining == 0 {
145            self.input.take();
146            return Poll::Ready(None);
147        }
148
149        let Some(input) = self.input.as_mut() else {
150            return Poll::Ready(None);
151        };
152
153        match input.poll_next_unpin(ctx) {
154            Poll::Ready(Some(Ok(batch))) => {
155                let num_rows = batch.num_rows();
156                if num_rows > self.remaining {
157                    let remaining = self.remaining;
158                    self.remaining = 0;
159                    self.input.take();
160                    Poll::Ready(Some(batch.slice(0, remaining)))
161                } else {
162                    self.remaining -= num_rows;
163                    if self.remaining == 0 {
164                        self.input.take();
165                    }
166                    Poll::Ready(Some(Ok(batch)))
167                }
168            }
169            Poll::Ready(None) => {
170                self.input.take();
171                Poll::Ready(None)
172            }
173            other => other,
174        }
175    }
176}
177
178impl RecordBatchStream for ChainedRecordBatchStream {
179    fn name(&self) -> &str {
180        "ChainedRecordBatchStream"
181    }
182
183    fn schema(&self) -> SchemaRef {
184        self.schema.clone()
185    }
186
187    fn output_ordering(&self) -> Option<&[OrderOption]> {
188        None
189    }
190
191    fn metrics(&self) -> Option<RecordBatchMetrics> {
192        self.metrics.load().as_ref().map(|m| m.as_ref().clone())
193    }
194}
195
196impl Stream for ChainedRecordBatchStream {
197    type Item = Result<RecordBatch>;
198
199    fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
200        self.sequence_poll(ctx)
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use std::pin::Pin;
207    use std::sync::Arc;
208
209    use datatypes::prelude::*;
210    use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
211    use datatypes::vectors::UInt32Vector;
212    use futures::Stream;
213    use futures::task::{Context, Poll};
214
215    use super::*;
216    use crate::adapter::RecordBatchMetrics;
217    use crate::{OrderOption, RecordBatchStream};
218
219    struct MockRecordBatchStream {
220        batch: Option<RecordBatch>,
221        schema: SchemaRef,
222    }
223
224    impl RecordBatchStream for MockRecordBatchStream {
225        fn schema(&self) -> SchemaRef {
226            self.schema.clone()
227        }
228
229        fn output_ordering(&self) -> Option<&[OrderOption]> {
230            None
231        }
232
233        fn metrics(&self) -> Option<RecordBatchMetrics> {
234            None
235        }
236    }
237
238    impl Stream for MockRecordBatchStream {
239        type Item = Result<RecordBatch>;
240
241        fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
242            let batch = self.batch.take();
243
244            if let Some(batch) = batch {
245                Poll::Ready(Some(Ok(batch)))
246            } else {
247                Poll::Ready(None)
248            }
249        }
250    }
251
252    #[tokio::test]
253    async fn test_limited_chained_stream() {
254        let column_schemas = vec![ColumnSchema::new(
255            "number",
256            ConcreteDataType::uint32_datatype(),
257            false,
258        )];
259
260        let schema = Arc::new(Schema::try_new(column_schemas).unwrap());
261        let first = RecordBatch::new(
262            schema.clone(),
263            [Arc::new(UInt32Vector::from_vec(vec![0, 1, 2])) as _],
264        )
265        .unwrap();
266        let second = RecordBatch::new(
267            schema.clone(),
268            [Arc::new(UInt32Vector::from_vec(vec![3, 4, 5])) as _],
269        )
270        .unwrap();
271        let chained = ChainedRecordBatchStream::new(vec![
272            Box::pin(MockRecordBatchStream {
273                schema: schema.clone(),
274                batch: Some(first),
275            }),
276            Box::pin(MockRecordBatchStream {
277                schema: schema.clone(),
278                batch: Some(second),
279            }),
280        ])
281        .unwrap();
282
283        let batches = collect(Box::pin(LimitedRecordBatchStream::new(
284            Box::pin(chained),
285            4,
286        )))
287        .await
288        .unwrap();
289
290        assert_eq!(2, batches.len());
291        assert_eq!(3, batches[0].num_rows());
292        assert_eq!(1, batches[1].num_rows());
293    }
294
295    #[tokio::test]
296    async fn test_limited_stream_with_zero_limit() {
297        let column_schemas = vec![ColumnSchema::new(
298            "number",
299            ConcreteDataType::uint32_datatype(),
300            false,
301        )];
302
303        let schema = Arc::new(Schema::try_new(column_schemas).unwrap());
304        let batch = RecordBatch::new(
305            schema.clone(),
306            [Arc::new(UInt32Vector::from_vec(vec![0])) as _],
307        )
308        .unwrap();
309        let stream = MockRecordBatchStream {
310            schema,
311            batch: Some(batch),
312        };
313
314        let batches = collect(Box::pin(LimitedRecordBatchStream::new(Box::pin(stream), 0)))
315            .await
316            .unwrap();
317
318        assert!(batches.is_empty());
319    }
320
321    #[tokio::test]
322    async fn test_collect() {
323        let column_schemas = vec![ColumnSchema::new(
324            "number",
325            ConcreteDataType::uint32_datatype(),
326            false,
327        )];
328
329        let schema = Arc::new(Schema::try_new(column_schemas).unwrap());
330
331        let stream = MockRecordBatchStream {
332            schema: schema.clone(),
333            batch: None,
334        };
335
336        let batches = collect(Box::pin(stream)).await.unwrap();
337        assert_eq!(0, batches.len());
338
339        let numbers: Vec<u32> = (0..10).collect();
340        let columns = [Arc::new(UInt32Vector::from_vec(numbers)) as _];
341        let batch = RecordBatch::new(schema.clone(), columns).unwrap();
342
343        let stream = MockRecordBatchStream {
344            schema: schema.clone(),
345            batch: Some(batch.clone()),
346        };
347        let batches = collect(Box::pin(stream)).await.unwrap();
348        assert_eq!(1, batches.len());
349        assert_eq!(batch, batches[0]);
350
351        let stream = MockRecordBatchStream {
352            schema: schema.clone(),
353            batch: Some(batch.clone()),
354        };
355        let batches = collect_batches(Box::pin(stream)).await.unwrap();
356        let expect_batches = RecordBatches::try_new(schema.clone(), vec![batch]).unwrap();
357        assert_eq!(expect_batches, batches);
358    }
359}