Skip to main content

rudb_exec/
lib.rs

1//! Operators, morsels, the scheduler, hash tables, sorting and spilling.
2//!
3//! Rank 12 in the layer rule. See `xtask/layers.toml` and `spec/18-package-layout.md`.
4//!
5//! This is tier 0 of `spec/08-codegen.md` section 8.1: a pull based tree of operators, one variant
6//! per logical operator the binder can produce, every one of them written the simplest way that is
7//! correct. Tier 0 is never removed and never optional, because it is the reference every faster
8//! tier is differentially tested against, and a reference that is clever is a reference nobody can
9//! read the answer out of when the clever tier disagrees with it.
10//!
11//! # What pull based means here and what it does not mean
12//!
13//! [`Operator::next`] returns the next [`Chunk`](rudb_vector::Chunk) or `None` when there are no
14//! more. A pipeline of a scan, a filter and a projection is three of those calls deep and nothing
15//! is materialized between them. An operator that cannot answer without seeing all of its input,
16//! which is the aggregate, the sort, the join build side, the distinct and the set operations, does
17//! all of its work on the first call to `next` and then hands out what it built one chunk at a
18//! time. That is what `spec/07-execution.md` calls a pipeline breaker and it is the boundary the
19//! morsel driven scheduler will later cut pipelines at.
20//!
21//! What this is not is the scheduler. There is one thread, there are no morsels, there is no
22//! spilling and the hash join is a nested loop. Every one of those is M1 or later work and every
23//! one of them replaces an operator here without changing the tree that builds it, because the
24//! thing that builds the tree is [`build`] and the thing it builds against is a trait with two
25//! methods.
26//!
27//! # Why a schema per operator
28//!
29//! A bound plan refers to columns by [`ColumnBinding`](rudb_plan::ColumnBinding), which is a table
30//! index and a position, and a chunk is a row of vectors with no names on it. Something has to turn
31//! one into the other, and that something is [`Schema`]: it is what an operator says it produces,
32//! it carries the binding alongside the name and the type, and [`Schema::position_of`] is the whole
33//! of expression column resolution. Building it is where the operators agree with the binder about
34//! what a table index means, and it is checked rather than assumed, because a schema that is one
35//! column out produces a wrong answer instead of an error.
36
37#![forbid(unsafe_code)]
38
39mod build;
40mod expr;
41mod group;
42mod join;
43mod key;
44mod operator;
45mod rows;
46mod schema;
47mod setop;
48mod sort;
49mod source;
50mod stream;
51
52#[cfg(test)]
53mod tests;
54
55pub use build::build;
56pub use expr::{evaluate, evaluate_all};
57pub use operator::Operator;
58pub use schema::Schema;