gluesql_core/executor/
delete.rs1use {
2 super::{
3 Payload, Referencing,
4 fetch::{fetch, fetch_columns},
5 },
6 crate::{
7 ast::{BinaryOperator, ForeignKey, ReferentialAction},
8 plan::ExprPlan,
9 result::Result,
10 store::{GStore, GStoreMut},
11 },
12 serde::Serialize,
13 std::rc::Rc,
14 thiserror::Error as ThisError,
15};
16
17#[derive(ThisError, Serialize, Debug, PartialEq, Eq)]
18pub enum DeleteError {
19 #[error("referencing column exists: {0}")]
20 ReferencingColumnExists(String),
21
22 #[error("Value not found on column: {0}")]
23 ValueNotFound(String),
24}
25
26pub fn delete<T: GStore + GStoreMut>(
27 storage: &mut T,
28 table_name: &str,
29 selection: Option<&ExprPlan>,
30) -> Result<Payload> {
31 let columns = Rc::from(fetch_columns(storage, table_name)?);
32 let referencings = storage
33 .fetch_referencings(table_name)?
34 .into_iter()
35 .map(|referencing| {
36 fetch_columns(storage, &referencing.table_name)
37 .map(Rc::from)
38 .map(|columns| (referencing, columns))
39 })
40 .collect::<Result<Vec<_>>>()?;
41
42 let mut keys = Vec::new();
43 for item in fetch(storage, table_name, columns, selection)? {
44 let (key, row) = item?;
45
46 for (referencing, columns) in &referencings {
47 let Referencing {
48 table_name: referencing_table_name,
49 foreign_key:
50 ForeignKey {
51 referencing_column_name,
52 referenced_column_name,
53 on_delete,
54 ..
55 },
56 } = referencing;
57
58 let value = row
59 .get_value(referenced_column_name)
60 .ok_or(DeleteError::ValueNotFound(referenced_column_name.clone()))?
61 .clone();
62
63 let expr = &ExprPlan::BinaryOp {
64 left: Box::new(ExprPlan::Identifier(referencing_column_name.clone())),
65 op: BinaryOperator::Eq,
66 right: Box::new(ExprPlan::Value(value)),
67 };
68
69 let mut referencing_rows = fetch(
70 storage,
71 referencing_table_name,
72 Rc::clone(columns),
73 Some(expr),
74 )?;
75
76 let referencing_row_exists = referencing_rows.next().transpose()?.is_some();
77 if referencing_row_exists && on_delete == &ReferentialAction::NoAction {
78 return Err(DeleteError::ReferencingColumnExists(format!(
79 "{referencing_table_name}.{referencing_column_name}"
80 ))
81 .into());
82 }
83 }
84
85 keys.push(key);
86 }
87 let num_keys = keys.len();
88
89 storage
90 .delete_data(table_name, keys)
91 .map(|()| Payload::Delete(num_keys))
92}