rudb_arrow/lib.rs
1//! Arrow interchange, zero copy where the layouts permit.
2//!
3//! Rank 5 in the layer rule. See `xtask/layers.toml` and `spec/18-package-layout.md`.
4//!
5//! Arrow is how a result leaves the process without being formatted first. Pandas, Polars, R, Java
6//! and every other client DuckDB has a binding for already read it, so a result that arrives as
7//! Arrow arrives as columns the caller can use rather than as text the caller has to parse. That is
8//! the whole reason this crate exists, and it is why `spec/13-client-api.md` names Arrow before it
9//! names any of the per language bindings.
10//!
11//! Three pieces:
12//!
13//! - [`DataType`], one of our types as Arrow names it, and the format string the C data interface
14//! spells that name with.
15//! - [`Array`], one column in Arrow's buffer layout.
16//! - [`RecordBatch`], a chunk of them with a [`Schema`] over the top.
17//!
18//! # Zero copy, and where it stops
19//!
20//! The crate's description promises zero copy where the layouts permit, and this first version
21//! copies everywhere, so the promise is worth being exact about. Our validity bitmap is already
22//! Arrow's, bit for bit, and a run of `i32` values is a run of `i32` values, so for those two
23//! buffers the copy here is a memcpy that a later change removes by handing over ownership of the
24//! pages instead. That change needs a buffer whose lifetime an FFI consumer can hold, which needs
25//! the buffer manager, which is M2 work. Writing the copying version first means the mapping itself
26//! is settled and tested before the lifetime question is opened.
27//!
28//! One buffer will never be zero copy, and that is `VARCHAR`. We store a 16 byte view with an
29//! inline prefix plus an arena, and Arrow's `u` is a run of offsets over one contiguous block of
30//! bytes in row order. Those are different data structures rather than different spellings of one,
31//! so the export builds the block. Arrow does have a view layout of its own now, and adopting it
32//! later would make this one free, which is a thing to measure rather than a thing to assume.
33//!
34//! # What is deliberately not here
35//!
36//! The FFI boundary. `ArrowArray` and `ArrowSchema` are C structs with release callbacks and raw
37//! pointers, and the release callback is the part that is easy to get wrong and expensive to debug.
38//! It belongs in `rudb-c-api` with the rest of the `unsafe`, built on top of these owned types,
39//! which this crate can then keep testing without a single raw pointer. [`DataType::format`] is
40//! here rather than there because the format string is defined by Arrow's document, so it is the
41//! part a test can check against that document rather than against our own opinion.
42//!
43//! The nested types. A list is offsets and a child array, a struct is a list of child arrays, and
44//! there is no child array to build one out of until `rudb-vector` has a nested vector.
45
46#![forbid(unsafe_code)]
47
48mod array;
49mod batch;
50mod types;
51
52pub use array::Array;
53pub use batch::RecordBatch;
54pub use types::{DataType, Field, Schema, TimeUnit};