tempest-engine 0.0.2

Relational database engine for TempestDB
Documentation
use std::path::PathBuf;

use tempest_core::tempest_str::TempestStr;
use tempest_core::test_utils::setup_tracing;
use tempest_io::VirtualIo;
use tempest_rt::block_on;

use crate::{Engine, config::EngineConfig, query::QueryResult, types::TempestValue};

// -- helpers --

/// Creates database `main`, type `main.User { id: Int64, username: String }`,
/// and table `main.users : main.User { primary key (id) }`.
async fn setup_schema(engine: &mut Engine<VirtualIo>) {
    engine.execute("create database main;").await.unwrap();
    engine
        .execute("create type main.User struct { id: Int64, username: String };")
        .await
        .unwrap();
    engine
        .execute("create table main.users : main.User { primary key (id) };")
        .await
        .unwrap();
}

pub(super) fn get_rows(result: &QueryResult) -> &Vec<Vec<TempestValue<'static>>> {
    let QueryResult::Rows { rows, .. } = result else {
        panic!("expected Rows, got {:?}", result)
    };
    rows
}

pub(super) fn get_columns(result: &QueryResult) -> &Vec<TempestStr<'static>> {
    let QueryResult::Rows { columns, .. } = result else {
        panic!("expected Rows, got {:?}", result)
    };
    columns
}

pub(super) async fn open_engine() -> Engine<VirtualIo> {
    Engine::<VirtualIo>::open(PathBuf::from("/tempest"), EngineConfig::default())
        .await
        .unwrap()
}

mod embedded_type_ref;
mod enums;
mod tuple_struct;

// -- DDL tests --

#[test]
fn test_create_database() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        let results = engine.execute("create database main;").await.unwrap();
        assert!(matches!(results[0], QueryResult::Empty));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_create_type() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        engine.execute("create database main;").await.unwrap();
        let results = engine
            .execute("create type main.User struct { id: Int64, username: String };")
            .await
            .unwrap();
        assert!(matches!(results[0], QueryResult::Empty));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_create_table() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        engine.execute("create database main;").await.unwrap();
        engine
            .execute("create type main.User struct { id: Int64, username: String };")
            .await
            .unwrap();
        let results = engine
            .execute("create table main.users : main.User { primary key (id) };")
            .await
            .unwrap();
        assert!(matches!(results[0], QueryResult::Empty));
        engine.shutdown().await.unwrap();
    });
}

// -- Generics tests --

#[test]
fn test_generic_type_as_table_type() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        engine.execute("create database main;").await.unwrap();
        engine
            .execute("create type main.Box[T] struct { value: T };")
            .await
            .unwrap();
        engine
            .execute("create table main.boxes : main.Box[Int64] { primary key (value) };")
            .await
            .unwrap();
        engine
            .execute("insert into main.boxes { value: 42 };")
            .await
            .unwrap();
        let results = engine.execute("select * from main.boxes;").await.unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], TempestValue::Int64(42));
        engine.shutdown().await.unwrap();
    });
}

// -- Insert tests --

#[test]
fn test_insert_single_row() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        let results = engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        assert!(matches!(results[0], QueryResult::Empty));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_insert_multiple_rows() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 2, username: "Jeff" };"#)
            .await
            .unwrap();
        let results = engine
            .execute(r#"insert into main.users { id: 3, username: "Bob" };"#)
            .await
            .unwrap();
        assert!(matches!(results[0], QueryResult::Empty));
        engine.shutdown().await.unwrap();
    });
}

// -- Select tests --

#[test]
fn test_select_all_columns() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        let results = engine.execute("select * from main.users;").await.unwrap();
        let cols = get_columns(&results[0]);
        let rows = get_rows(&results[0]);
        assert_eq!(cols[0], "id".into());
        assert_eq!(cols[1], "username".into());
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], TempestValue::Int64(1));
        assert_eq!(rows[0][1], TempestValue::String("John".into()));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_select_projection() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        let results = engine
            .execute("select username from main.users;")
            .await
            .unwrap();
        let cols = get_columns(&results[0]);
        let rows = get_rows(&results[0]);
        assert_eq!(cols.len(), 1);
        assert_eq!(cols[0], "username".into());
        assert_eq!(rows[0][0], TempestValue::String("John".into()));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_select_where_eq() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 2, username: "Jeff" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 3, username: "Bob" };"#)
            .await
            .unwrap();
        let results = engine
            .execute("select * from main.users where id = 2;")
            .await
            .unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], TempestValue::Int64(2));
        assert_eq!(rows[0][1], TempestValue::String("Jeff".into()));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_select_where_gte() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 2, username: "Jeff" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 3, username: "Bob" };"#)
            .await
            .unwrap();
        let results = engine
            .execute("select username from main.users where id >= 2;")
            .await
            .unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0][0], TempestValue::String("Jeff".into()));
        assert_eq!(rows[1][0], TempestValue::String("Bob".into()));
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_select_empty_table() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        let results = engine.execute("select * from main.users;").await.unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 0);
        engine.shutdown().await.unwrap();
    });
}

// -- Delete tests --

#[test]
fn test_delete_all() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 2, username: "Jeff" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 3, username: "Bob" };"#)
            .await
            .unwrap();
        let del_results = engine.execute("delete from main.users;").await.unwrap();
        assert!(matches!(del_results[0], QueryResult::RowsChanged(3)));
        let results = engine.execute("select * from main.users;").await.unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 0, "all rows should have been deleted");
        engine.shutdown().await.unwrap();
    });
}

#[test]
fn test_delete_where() {
    setup_tracing();
    block_on(VirtualIo::default(), async {
        let mut engine = open_engine().await;
        setup_schema(&mut engine).await;
        engine
            .execute(r#"insert into main.users { id: 1, username: "John" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 2, username: "Jeff" };"#)
            .await
            .unwrap();
        engine
            .execute(r#"insert into main.users { id: 3, username: "Bob" };"#)
            .await
            .unwrap();
        let del_results = engine
            .execute("delete from main.users where id = 2;")
            .await
            .unwrap();
        assert!(matches!(del_results[0], QueryResult::RowsChanged(1)));
        let results = engine.execute("select * from main.users;").await.unwrap();
        let rows = get_rows(&results[0]);
        assert_eq!(rows.len(), 2, "only id=2 should have been deleted");
        assert_eq!(rows[0][0], TempestValue::Int64(1));
        assert_eq!(rows[1][0], TempestValue::Int64(3));
        engine.shutdown().await.unwrap();
    });
}