matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use std::collections::HashMap;

use anyhow::{bail, Context as AnyhowContext, Result};

use crate::{
    ast::{BinOp, Column, Expr, UnOp},
    value::Value,
};

#[derive(Clone, Debug)]
pub struct Context {
    bindings: Vec<Value>,

    values: Vec<Value>,

    col_to_idx: HashMap<Column, usize>,
    idx_to_col: Vec<Column>,
}

impl Context {
    pub fn new(bindings: Vec<Value>) -> Context {
        Context {
            bindings,
            values: Vec::new(),
            col_to_idx: HashMap::new(),
            idx_to_col: Vec::new(),
        }
    }

    pub fn set(&mut self, col: Column, value: Value) {
        let this_id = self.values.len();

        self.values.push(value);

        self.idx_to_col.push(col.clone());

        self.col_to_idx.insert(col, this_id);
    }

    pub fn get(&self, col: &Column) -> Option<Value> {
        Some(self.values[*self.col_to_idx.get(col)?].clone())
    }

    pub fn eval(&self, expr: &Expr) -> Result<Value> {
        Ok(match expr {
            Expr::Literal(v) => v.clone(),
            Expr::Binding(b) => self
                .bindings
                .get(*b as usize)
                .cloned()
                .with_context(|| format!("binding out of range: {b}"))?,
            Expr::Column(col) => self
                .get(&col)
                .with_context(|| format!("column {col} not found"))?,
            Expr::Bin(lhs, op, rhs) => match (self.eval(lhs)?, op, self.eval(rhs)?) {
                (Value::Null, BinOp::Eq, Value::Null) => Value::Bool(true),
                (lhs, BinOp::Eq, rhs) => Value::Bool(lhs == rhs),
                (lhs, BinOp::NEq, rhs) => Value::Bool(lhs != rhs),
                (Value::Null, BinOp::Gt, Value::Null) => Value::Bool(false),
                (lhs, BinOp::Gt, rhs) => Value::Bool(lhs > rhs),
                (lhs, BinOp::GtEq, rhs) => Value::Bool(lhs >= rhs),
                (lhs, BinOp::Lt, rhs) => Value::Bool(lhs < rhs),
                (lhs, BinOp::LtEq, rhs) => Value::Bool(lhs <= rhs),
                (Value::Bool(lhs), BinOp::And, Value::Bool(rhs)) => Value::Bool(lhs && rhs),
                (Value::Bool(lhs), BinOp::Or, Value::Bool(rhs)) => Value::Bool(lhs || rhs),
                (Value::Int(lhs), BinOp::Add, Value::Int(rhs)) => Value::Int(lhs + rhs),
                (Value::Int(lhs), BinOp::Sub, Value::Int(rhs)) => Value::Int(lhs - rhs),
                (Value::Int(lhs), BinOp::Mul, Value::Int(rhs)) => Value::Int(lhs * rhs),
                (Value::Int(lhs), BinOp::Div, Value::Int(rhs)) => Value::Int(lhs / rhs),
                (Value::Int(lhs), BinOp::Mod, Value::Int(rhs)) => Value::Int(lhs % rhs),
                (lhs, BinOp::And, rhs)
                | (lhs, BinOp::Or, rhs)
                | (lhs, BinOp::Add, rhs)
                | (lhs, BinOp::Sub, rhs)
                | (lhs, BinOp::Mul, rhs)
                | (lhs, BinOp::Div, rhs)
                | (lhs, BinOp::Mod, rhs) => {
                    bail!("the {op} operator is not defined for values {lhs} and {rhs}")
                }
            },
            Expr::Unary(op, rhs) => match (op, self.eval(rhs)?) {
                (UnOp::Not, Value::Bool(rhs)) => Value::Bool(!rhs),
                (UnOp::Not, _) => {
                    bail!("the {op} operator is not defined for value {rhs}")
                }
            },
            Expr::Edge(_, _, _, e) => self.eval(e)?,
        })
    }

    pub fn as_slice(&self) -> &[Value] {
        &self.values
    }
}