vortex_scan/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4#![deny(missing_docs)]
5
6//! The Vortex Scan API implements an abstract table scan interface that can be used to
7//! read data from various data sources.
8//!
9//! It supports arbitrary projection expressions, filter expressions, and limit pushdown as well
10//! as mechanisms for parallel and distributed execution via partitions.
11//!
12//! The API is currently under development and may change in future releases, however we hope to
13//! stabilize into stable C ABI for use within foreign language bindings.
14//!
15//! If you are looking to scan Vortex files or layouts, the Vortex implementation of the Scan API
16//! can be found in the `vortex-layout` crate.
17//!
18//! ## Open Issues
19//!
20//! * We probably want to make the DataSource serializable as well, so that we can share
21//! source-level state with workers, separately from partition serialization.
22//! * We should add a way for the client to negotiate capabilities with the data source, for
23//! example which encodings it knows about.
24
25pub mod row_mask;
26pub mod selection;
27pub mod strict_sorted_buffer;
28
29use std::any::Any;
30use std::ops::Range;
31use std::sync::Arc;
32
33use async_trait::async_trait;
34use futures::stream::BoxStream;
35use selection::Selection;
36use vortex_array::dtype::DType;
37use vortex_array::dtype::FieldPath;
38use vortex_array::expr::Expression;
39use vortex_array::expr::root;
40use vortex_array::expr::stats::Precision;
41use vortex_array::stats::StatsSet;
42use vortex_array::stream::SendableArrayStream;
43use vortex_error::VortexResult;
44use vortex_error::vortex_bail;
45use vortex_session::VortexSession;
46
47/// A sendable stream of partitions.
48pub type PartitionStream = BoxStream<'static, VortexResult<PartitionRef>>;
49
50/// Opens a Vortex [`DataSource`] from a URI.
51///
52/// Configuration can be passed via the URI query parameters, similar to JDBC / ADBC.
53/// Providers can be registered with the [`VortexSession`] to support additional URI schemes.
54#[async_trait]
55pub trait DataSourceOpener: 'static {
56 /// Attempt to open a new data source from a URI.
57 async fn open(&self, uri: String, session: &VortexSession) -> VortexResult<DataSourceRef>;
58}
59
60/// Supports deserialization of a Vortex [`DataSource`] on a remote worker.
61#[async_trait]
62pub trait DataSourceRemote: 'static {
63 /// Attempt to deserialize the source.
64 fn deserialize_data_source(
65 &self,
66 data: &[u8],
67 session: &VortexSession,
68 ) -> VortexResult<DataSourceRef>;
69}
70
71/// A reference-counted data source.
72pub type DataSourceRef = Arc<dyn DataSource>;
73
74/// A data source represents a streamable dataset that can be scanned with projection and filter
75/// expressions. Each scan produces partitions that can be executed in parallel to read data. Each
76/// partition can be serialized for remote execution.
77///
78/// The DataSource may be used multiple times to create multiple scans, whereas each scan and each
79/// partition of a scan can only be consumed once.
80///
81/// Partitions have indices. Partition index is stable throughout DataSource's
82/// lifetime. For every scan requested on the DataSource, the index will stay
83/// the same.
84/// However, this means you should create another instance of a DataSource if
85/// your environment changes e.g. you have a glob and another file is added to
86/// the filesystem this glob references.
87/// See MultiFileDataSource in vortex-file/src/multi/mod.rs
88#[async_trait]
89pub trait DataSource: 'static + Send + Sync {
90 /// Returns the dtype of the source.
91 fn dtype(&self) -> &DType;
92
93 /// Returns an estimate of the row count of the un-filtered source.
94 fn row_count(&self) -> Precision<u64> {
95 Precision::Absent
96 }
97
98 /// Returns an estimate of the byte size of the un-filtered source.
99 fn byte_size(&self) -> Precision<u64> {
100 Precision::Absent
101 }
102
103 /// Serialize the [`DataSource`] to pass to a remote worker.
104 fn serialize(&self) -> VortexResult<Option<Vec<u8>>> {
105 Ok(None)
106 }
107
108 /// Deserialize a partition that was previously serialized from a compatible data source.
109 fn deserialize_partition(
110 &self,
111 data: &[u8],
112 session: &VortexSession,
113 ) -> VortexResult<PartitionRef> {
114 let _ = (data, session);
115 vortex_bail!("DataSource does not support deserialization")
116 }
117
118 /// Returns a scan over the source.
119 async fn scan(&self, scan_request: ScanRequest) -> VortexResult<DataSourceScanRef>;
120
121 /// Returns the statistics for a given field.
122 async fn field_statistics(&self, field_path: &FieldPath) -> VortexResult<StatsSet>;
123}
124
125/// A request to scan a data source.
126#[derive(Debug, Clone)]
127pub struct ScanRequest {
128 /// Projection expression. Defaults to `root()` which returns all columns.
129 pub projection: Expression,
130 /// Filter expression, `None` implies no filter.
131 pub filter: Option<Expression>,
132 /// The per-partition row range to read. Row range will be applied
133 /// over every partition you scan.
134 pub row_range: Option<Range<u64>>,
135 /// The per-partition row selection to read. Row selection will be applied
136 /// over every partition you scan.
137 pub selection: Selection,
138 /// Partition selection to scan, which allows readers to skip unwanted partitions.
139 pub partition_selection: Selection,
140 /// Partition range to scan, which allows readers to skip unwanted partitions.
141 pub partition_range: Option<Range<u64>>,
142 /// Whether the scan should preserve row order. If false, the scan may produce rows in any
143 /// order, for example to enable parallel execution across partitions.
144 pub ordered: bool,
145 /// Optional limit on the number of rows returned by scan. Limits are applied after all
146 /// filtering and row selection.
147 pub limit: Option<u64>,
148}
149
150impl Default for ScanRequest {
151 fn default() -> Self {
152 Self {
153 projection: root(),
154 filter: None,
155 row_range: None,
156 selection: Selection::default(),
157 partition_selection: Selection::default(),
158 ordered: false,
159 limit: None,
160 partition_range: None,
161 }
162 }
163}
164
165/// A boxed data source scan.
166pub type DataSourceScanRef = Box<dyn DataSourceScan>;
167
168/// A data source scan produces partitions that can be executed to read data from the source.
169pub trait DataSourceScan: 'static + Send {
170 /// The returned dtype of the scan.
171 fn dtype(&self) -> &DType;
172
173 /// Returns an estimate of the total number of partitions the scan will produce.
174 fn partition_count(&self) -> Precision<usize>;
175
176 /// Returns a stream of partitions to be processed.
177 fn partitions(self: Box<Self>) -> PartitionStream;
178}
179
180/// A reference-counted partition.
181pub type PartitionRef = Box<dyn Partition>;
182
183/// A partition represents a unit of work that can be executed to produce a stream of arrays.
184pub trait Partition: 'static + Send {
185 /// Downcast the partition to a concrete type.
186 fn as_any(&self) -> &dyn Any;
187
188 /// Some unique identifier for partition.
189 /// If you have an instance of a DataSource, the indices of emitted
190 /// partitions will stay stable for every scan in this DataSource.
191 fn index(&self) -> usize;
192
193 /// Returns an estimate of the row count for this partition.
194 fn row_count(&self) -> Precision<u64>;
195
196 /// Returns an estimate of the byte size for this partition.
197 fn byte_size(&self) -> Precision<u64>;
198
199 /// Serialize this partition for a remote worker.
200 fn serialize(&self) -> VortexResult<Option<Vec<u8>>> {
201 Ok(None)
202 }
203
204 /// Executes the partition, returning an array stream.
205 ///
206 /// This method must be fast. The returned stream should be lazy — all non-trivial work
207 /// (I/O, decoding, filtering) must be deferred to when the stream is polled. Expensive
208 /// operations should be spawned onto the runtime to enable parallel execution across
209 /// threads.
210 fn execute(self: Box<Self>) -> VortexResult<SendableArrayStream>;
211}