DEVELOPER

Rust API

Embed Teide in your Rust application using the Context, Session, Table, Graph, and Column types.

Quick Start

use teide::sql::{Session, ExecResult};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut session = Session::new()?;

    session.execute("CREATE TABLE t AS SELECT * FROM read_csv('data.csv')")?;

    match session.execute("SELECT name, SUM(val) AS total FROM t GROUP BY name ORDER BY total DESC")? {
        ExecResult::Query(r) => {
            println!("{} rows, {} columns", r.table.nrows(), r.columns.len());
        }
        ExecResult::Ddl(msg) => println!("{msg}"),
    }
    Ok(())
}

Context

The engine runtime. Manages heap, symbol table, and thread pool.

use teide::Context;

let ctx = Context::new()?;
let table = ctx.read_csv("data.csv")?;
let table = ctx.read_csv_opts("data.tsv", '\t', true, None)?;
let table = ctx.read_splayed("/path/to/table")?;
let table = ctx.read_parted("/path/to/db", "trades")?;

Important: Only one Context should exist at a time. Context is !Send + !Sync (C engine uses thread-local heaps).

Table

A columnar table with ref-counted storage.

let table = ctx.read_csv("data.csv")?;

// Metadata
println!("{} rows, {} cols", table.nrows(), table.ncols());
println!("Column 0: {}", table.col_name_str(0));
println!("Type: {}", table.col_type(0));

// Read values
let val: i64 = table.get_i64(0, 0).unwrap();
let val: f64 = table.get_f64(1, 0).unwrap();
let val: String = table.get_str(0, 0).unwrap();

// Manipulate
let renamed = table.with_column_names(&["a".into(), "b".into()])?;
let subset = table.pick_columns(&["name", "score"])?;
table.write_csv("output.csv")?;

Graph

A lazy operation DAG bound to a table. Operations build a computation graph that is optimized and executed on demand.

let ctx = Context::new()?;
let table = ctx.read_csv("data.csv")?;
let mut g = ctx.graph(&table)?;

// Build computation graph
let tbl = g.const_table(&table)?;
let v1 = g.scan("v1")?;
let threshold = g.const_i64(3)?;
let pred = g.gt(v1, threshold)?;
let filtered = g.filter(tbl, pred)?;
let result = g.execute(filtered)?;

Aggregation:

let id1 = g.scan("id1")?;
let v1 = g.scan("v1")?;
let grouped = g.group_by(
    &[id1],
    &[teide::AggOp::Sum],
    &[v1],
)?;
let result = g.execute(grouped)?;

Available operations:

Session (SQL API)

A stateful SQL session that maintains a table registry.

use teide::sql::{Session, ExecResult};

let mut session = Session::new()?;

// DDL
session.execute("CREATE TABLE t (id INTEGER, name VARCHAR)")?;
session.execute("INSERT INTO t VALUES (1, 'alice'), (2, 'bob')")?;

// Query
match session.execute("SELECT * FROM t ORDER BY id")? {
    ExecResult::Query(result) => {
        for col in &result.columns {
            print!("{col}\t");
        }
        println!();
        println!("{} rows", result.table.nrows());
    }
    ExecResult::Ddl(msg) => println!("{msg}"),
}

// Inspect tables
for name in session.table_names() {
    if let Some((rows, cols)) = session.table_info(name) {
        println!("{name}: {rows} rows, {cols} cols");
    }
}

Error Handling

use teide::Error;

match result {
    Err(Error::Oom) => eprintln!("Out of memory"),
    Err(Error::Type) => eprintln!("Type mismatch"),
    Err(Error::Schema) => eprintln!("Schema error"),
    Err(e) => eprintln!("Error: {e:?}"),
    Ok(val) => { /* success */ }
}

Error variants: Oom, Type, Range, Length, Rank, Domain, Nyi, Io, Schema, Corrupt, Cancel, InvalidInput, NullPointer, EngineNotInitialized, RuntimeUnavailable.