Skip to main content

gluesql_core/executor/
delete.rs

1use {
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}