matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use anyhow::{bail, Result};

use crate::{catalog::Index, eval::Context, kv::RangeIterKV, Db, Value};

use super::{execute_plan_next, PlanNode};

#[derive(Debug)]
pub struct Delete {
    table: String,
    child: Box<PlanNode>,
}

impl Delete {
    pub fn new(table: String, child: PlanNode) -> Self {
        Self {
            table,
            child: Box::new(child),
        }
    }

    pub fn execute(&mut self, db: &mut Db, ctx: Context) -> Result<Option<Context>> {
        let schema = db.get_table_schema(db.this_tx_id, &self.table)?;
        let Some(ctx) = execute_plan_next(&mut self.child, db, ctx)? else {
            return Ok(None);
        };

        let primary_key = schema
            .primary_key
            .iter()
            .map(|e| ctx.eval(e))
            .collect::<Result<Vec<_>, _>>()?;

        let secondary_keys = schema
            .indexes
            .iter()
            .map(|index| -> Result<Vec<Value>> {
                Ok(index
                    .exprs
                    .iter()
                    .map(|e| ctx.eval(e))
                    .collect::<Result<Vec<_>, _>>()?
                    .into_iter()
                    .chain(primary_key.iter().cloned())
                    .collect::<Vec<_>>())
            })
            .collect::<Result<Vec<_>>>()?;

        for reference in &schema.referenced_by {
            let ref_schema = db.get_table_schema(db.this_tx_id, &reference)?;
            for foreign_key in ref_schema
                .foreign_keys
                .iter()
                .filter(|f| f.rhs_table == schema.name)
            {
                let sk = foreign_key
                    .rhs_exprs
                    .iter()
                    .map(|e| ctx.eval(e))
                    .collect::<Result<Vec<_>, _>>()?;

                if RangeIterKV::new_simple(
                    if ref_schema.primary_key == foreign_key.lhs_exprs {
                        ref_schema.name.clone()
                    } else {
                        Index {
                            exprs: foreign_key.lhs_exprs.clone(),
                            unique: false,
                        }
                        .name(&ref_schema.name)
                    },
                    db.this_tx_id,
                    sk.clone(),
                )
                .next(db)?
                .is_some()
                {
                    bail!("row is referenced by table {}", reference)
                }
            }
        }

        db.delete_key(&self.table, primary_key)?;

        for (sk, index) in secondary_keys.into_iter().zip(schema.indexes.iter()) {
            db.delete_key(&index.name(&schema.name), sk)?;
        }

        Ok(Some(ctx))
    }
}