Skip to content

Commit

Permalink
move test version of build_query to test code
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril committed May 13, 2024
1 parent dbbf771 commit 24686b5
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 110 deletions.
128 changes: 60 additions & 68 deletions crates/vm/src/eval.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,12 @@
use crate::errors::ErrorVm;
use crate::expr::{Code, ColumnOp, JoinExpr, ProjectExpr, SourceExpr, SourceSet};
use crate::expr::{Expr, Query};
use crate::iterators::RelIter;
use crate::expr::{Code, ColumnOp, Expr, JoinExpr, ProjectExpr, SourceSet};
use crate::program::{ProgramVm, Sources};
use crate::rel_ops::RelOps;
use crate::relation::RelValue;
use spacetimedb_sats::ProductValue;

pub type IterRows<'a> = dyn RelOps<'a> + 'a;

/// `sources` should be a `Vec`
/// where the `idx`th element is the table referred to in the `query` as `SourceId(idx)`.
/// While constructing the query, the `sources` will be destructively modified with `Option::take`
/// to extract the sources,
/// so the `query` cannot refer to the same `SourceId` multiple times.
pub fn build_query<'a, const N: usize>(
mut result: Box<IterRows<'a>>,
query: &'a [Query],
sources: Sources<'_, N>,
) -> Result<Box<IterRows<'a>>, ErrorVm> {
for q in query {
result = match q {
Query::IndexScan(_) => {
panic!("index scans unsupported on memory tables")
}
Query::IndexJoin(_) => {
panic!("index joins unsupported on memory tables")
}
Query::Select(cmp) => build_select(result, cmp),
Query::Project(proj) => build_project(result, proj),
Query::JoinInner(q) => {
let rhs = build_source_expr_query(sources, &q.rhs.source);
let rhs = build_query(rhs, &q.rhs.query, sources)?;
join_inner(result, rhs, q)
}
};
}
Ok(result)
}

pub fn build_select<'a>(base: impl RelOps<'a> + 'a, cmp: &'a ColumnOp) -> Box<IterRows<'a>> {
Box::new(base.select(move |row| cmp.compare(row)))
}
Expand All @@ -63,17 +31,6 @@ pub fn join_inner<'a>(lhs: impl RelOps<'a> + 'a, rhs: impl RelOps<'a> + 'a, q: &
}
}

pub(crate) fn build_source_expr_query<'a, const N: usize>(
sources: Sources<'_, N>,
source: &SourceExpr,
) -> Box<IterRows<'a>> {
let source_id = source.source_id().unwrap_or_else(|| todo!("How pass the db iter?"));
let table = sources.take(source_id).unwrap_or_else(|| {
panic!("Query plan specifies in-mem table for {source_id:?}, but found a `DbTable` or nothing")
});
Box::new(RelIter::new(table.into_iter().map(RelValue::Projection)))
}

/// Execute the code
pub fn eval<const N: usize, P: ProgramVm>(p: &mut P, code: Code, sources: Sources<'_, N>) -> Code {
match code {
Expand Down Expand Up @@ -219,17 +176,63 @@ pub mod tests {

use super::test_helpers::*;
use super::*;
use crate::expr::{QueryExpr, SourceSet};
use crate::program::Program;
use crate::expr::{CrudExpr, Query, QueryExpr, SourceExpr, SourceSet};
use crate::iterators::RelIter;
use crate::relation::MemTable;
use spacetimedb_lib::operator::{OpCmp, OpLogic};
use spacetimedb_primitives::ColId;
use spacetimedb_sats::db::error::RelationError;
use spacetimedb_sats::relation::{FieldName, Header};
use spacetimedb_sats::{product, AlgebraicType, ProductType};

fn run_query<const N: usize>(p: &mut Program, ast: Expr, sources: SourceSet<Vec<ProductValue>, N>) -> MemTable {
match run_ast(p, ast, sources) {
/// From an original source of `result`s, applies `queries` and returns a final set of results.
fn build_query<'a, const N: usize>(
mut result: Box<IterRows<'a>>,
queries: &'a [Query],
sources: Sources<'_, N>,
) -> Box<IterRows<'a>> {
for q in queries {
result = match q {
Query::IndexScan(_) | Query::IndexJoin(_) => panic!("unsupported on memory tables"),
Query::Select(cmp) => build_select(result, cmp),
Query::Project(proj) => build_project(result, proj),
Query::JoinInner(q) => {
let rhs = build_source_expr_query(sources, &q.rhs.source);
let rhs = build_query(rhs, &q.rhs.query, sources);
join_inner(result, rhs, q)
}
};
}
result
}

fn build_source_expr_query<'a, const N: usize>(sources: Sources<'_, N>, source: &SourceExpr) -> Box<IterRows<'a>> {
let source_id = source.source_id().unwrap();
let table = sources.take(source_id).unwrap();
Box::new(RelIter::new(table.into_iter().map(RelValue::Projection)))
}

/// A default program that run in-memory without a database
struct Program;

impl ProgramVm for Program {
fn eval_query<const N: usize>(&mut self, query: CrudExpr, sources: Sources<'_, N>) -> Result<Code, ErrorVm> {
match query {
CrudExpr::Query(query) => {
let result = build_source_expr_query(sources, &query.source);
let rows = build_query(result, &query.query, sources).collect_vec(|row| row.into_product_value());

let head = query.head().clone();

Ok(Code::Table(MemTable::new(head, query.source.table_access(), rows)))
}
_ => todo!(),
}
}
}

fn run_query<const N: usize>(ast: Expr, sources: SourceSet<Vec<ProductValue>, N>) -> MemTable {
match run_ast(&mut Program, ast, sources) {
Code::Table(x) => x,
x => panic!("Unexpected result on query: {x}"),
}
Expand All @@ -241,7 +244,6 @@ pub mod tests {

#[test]
fn test_select() {
let p = &mut Program;
let input = mem_table_one_u64(0.into());
let field = get_field_pos(&input, 0);
let mut sources = SourceSet::<_, 1>::empty();
Expand All @@ -253,7 +255,7 @@ pub mod tests {

let head = q.head().clone();

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);
let row = product![1u64];
assert_eq!(result, MemTable::from_iter(head, [row]), "Query");
}
Expand Down Expand Up @@ -294,7 +296,6 @@ pub mod tests {

#[test]
fn test_join_inner() {
let p = &mut Program;
let table_id = 0.into();
let table = mem_table_one_u64(table_id);
let col: ColId = 0.into();
Expand All @@ -306,7 +307,7 @@ pub mod tests {

let q = QueryExpr::new(source_expr).with_join_inner(second_source_expr, col, col, false);
dbg!(&q);
let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

// The expected result.
let head = Header::new(table_id, "".into(), [field.clone(), field].into(), Vec::new());
Expand All @@ -324,7 +325,6 @@ pub mod tests {

#[test]
fn test_semijoin() {
let p = &mut Program;
let table_id = 0.into();
let table = mem_table_one_u64(table_id);
let col = 0.into();
Expand All @@ -334,7 +334,7 @@ pub mod tests {
let second_source_expr = sources.add_mem_table(table);

let q = QueryExpr::new(source_expr).with_join_inner(second_source_expr, col, col, true);
let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

// The expected result.
let inv = ProductType::from([(None, AlgebraicType::U64)]);
Expand All @@ -352,8 +352,6 @@ pub mod tests {

#[test]
fn test_query_logic() {
let p = &mut Program;

let inv = ProductType::from([("id", AlgebraicType::U64), ("name", AlgebraicType::String)]);

let row = product![1u64, "health"];
Expand All @@ -368,7 +366,7 @@ pub mod tests {
.with_select_cmp(OpLogic::And, scalar(true), scalar(true))
.unwrap();

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

assert_eq!(result, inv.clone(), "Query And");

Expand All @@ -379,7 +377,7 @@ pub mod tests {
.with_select_cmp(OpLogic::Or, scalar(true), scalar(false))
.unwrap();

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

assert_eq!(result, inv, "Query Or");
}
Expand All @@ -388,8 +386,6 @@ pub mod tests {
/// Inventory
/// | id: u64 | name : String |
fn test_query_inner_join() {
let p = &mut Program;

let inv = ProductType::from([("id", AlgebraicType::U64), ("name", AlgebraicType::String)]);

let row = product![1u64, "health"];
Expand All @@ -404,7 +400,7 @@ pub mod tests {

let q = QueryExpr::new(source_expr).with_join_inner(second_source_expr, col, col, false);

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

//The expected result
let inv = ProductType::from([
Expand All @@ -421,8 +417,6 @@ pub mod tests {
/// Inventory
/// | id: u64 | name : String |
fn test_query_semijoin() {
let p = &mut Program;

let inv = ProductType::from([("id", AlgebraicType::U64), ("name", AlgebraicType::String)]);

let row = product![1u64, "health"];
Expand All @@ -437,7 +431,7 @@ pub mod tests {

let q = QueryExpr::new(source_expr).with_join_inner(second_source_expr, col, col, true);

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

// The expected result.
let inv = ProductType::from([(None, AlgebraicType::U64), (Some("name"), AlgebraicType::String)]);
Expand All @@ -454,8 +448,6 @@ pub mod tests {
/// Location
/// | entity_id: u64 | x : f32 | z : f32 |
fn test_query_game() {
let p = &mut Program;

// See table above.
let data = create_game_data();
let inv @ [inv_inventory_id, _] = [0, 1].map(|c| c.into());
Expand Down Expand Up @@ -490,7 +482,7 @@ pub mod tests {
.with_select_cmp(OpCmp::LtEq, loc_field(location_z), scalar(32.0f32))
.unwrap();

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

let ty = ProductType::from([("entity_id", AlgebraicType::U64), ("inventory_id", AlgebraicType::U64)]);
let row1 = product!(100u64, 1u64);
Expand Down Expand Up @@ -538,7 +530,7 @@ pub mod tests {
.with_project(inv.map(inv_expr).into(), Some(inv_table_id))
.unwrap();

let result = run_query(p, q.into(), sources);
let result = run_query(q.into(), sources);

let ty = ProductType::from([("inventory_id", AlgebraicType::U64), ("name", AlgebraicType::String)]);
let row1 = product!(1u64, "health");
Expand Down
42 changes: 0 additions & 42 deletions crates/vm/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
//! It carries an [EnvDb] with the functions, idents, types.

use crate::errors::ErrorVm;
use crate::eval::{build_query, build_source_expr_query};
use crate::expr::{Code, CrudExpr, SourceSet};
use crate::rel_ops::RelOps;
use crate::relation::MemTable;
use spacetimedb_sats::ProductValue;

/// A trait to allow split the execution of `programs` to allow executing
Expand All @@ -22,42 +19,3 @@ pub trait ProgramVm {
}

pub type Sources<'a, const N: usize> = &'a mut SourceSet<Vec<ProductValue>, N>;

/// A default program that run in-memory without a database
pub struct Program;

impl ProgramVm for Program {
fn eval_query<const N: usize>(&mut self, query: CrudExpr, sources: Sources<'_, N>) -> Result<Code, ErrorVm> {
match query {
CrudExpr::Query(query) => {
let result = build_source_expr_query(sources, &query.source);
let rows = build_query(result, &query.query, sources)?.collect_vec(|row| row.into_product_value());

let head = query.head().clone();

Ok(Code::Table(MemTable::new(head, query.source.table_access(), rows)))
}
CrudExpr::Insert { .. } => {
todo!()
}
CrudExpr::Update { .. } => {
todo!()
}
CrudExpr::Delete { .. } => {
todo!()
}
CrudExpr::CreateTable { .. } => {
todo!()
}
CrudExpr::Drop { .. } => {
todo!()
}
CrudExpr::SetVar { .. } => {
todo!()
}
CrudExpr::ReadVar { .. } => {
todo!()
}
}
}
}

0 comments on commit 24686b5

Please sign in to comment.