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))
}
}