Skip to main content

duckdb/
arrow_batch.rs

1use super::{
2    Statement,
3    arrow::{datatypes::SchemaRef, record_batch::RecordBatch},
4};
5
6/// A handle for iterating the [`RecordBatch`]es of a query result.
7///
8/// The iterator is lazy: each batch is fetched only as the iterator is
9/// advanced. Whether the full result is materialized up front or fetched
10/// lazily in chunks depends on how the statement was executed — see
11/// [`query_arrow`](Statement::query_arrow) and
12/// [`stream_arrow`](Statement::stream_arrow).
13///
14/// The iterator panics if DuckDB fails to fetch a result chunk or convert it
15/// to Arrow.
16#[must_use = "Arrow is lazy and will do nothing unless consumed"]
17pub struct Arrow<'stmt> {
18    pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
19}
20
21#[allow(clippy::needless_lifetimes)]
22impl<'stmt> Arrow<'stmt> {
23    #[inline]
24    pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Self {
25        Arrow { stmt: Some(stmt) }
26    }
27
28    /// Return the Arrow schema reported by DuckDB after execution.
29    #[inline]
30    pub fn get_schema(&self) -> SchemaRef {
31        self.stmt
32            .expect("Arrow iterator always holds a statement")
33            .stmt
34            .schema()
35    }
36}
37
38#[allow(clippy::needless_lifetimes)]
39impl<'stmt> Iterator for Arrow<'stmt> {
40    type Item = RecordBatch;
41
42    fn next(&mut self) -> Option<Self::Item> {
43        match self.stmt?.step() {
44            Ok(Some(array)) => Some(RecordBatch::from(&array)),
45            Ok(None) => None,
46            Err(err) => panic!("Failed to fetch Arrow record batch: {err}"),
47        }
48    }
49}
50
51/// A handle for the resulting RecordBatch of a streaming query.
52///
53/// Identical to [`Arrow`]: the streaming behavior comes from how the
54/// statement was executed, not from the iterator type.
55pub type ArrowStream<'stmt> = Arrow<'stmt>;