gluesql_core/executor/
delete.rs

1use {
2    super::{
3        Payload, Referencing,
4        fetch::{fetch, fetch_columns},
5    },
6    crate::{
7        ast::{BinaryOperator, Expr, ForeignKey, ReferentialAction},
8        result::{Error, Result},
9        store::{GStore, GStoreMut},
10    },
11    futures::stream::{StreamExt, TryStreamExt},
12    serde::Serialize,
13    std::sync::Arc,
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 async fn delete<T: GStore + GStoreMut>(
27    storage: &mut T,
28    table_name: &str,
29    selection: &Option<Expr>,
30) -> Result<Payload> {
31    let columns = fetch_columns(storage, table_name).await?.map(Arc::from);
32    let referencings = storage.fetch_referencings(table_name).await?;
33    let keys = fetch(storage, table_name, columns, selection.as_ref())
34        .await?
35        .into_stream()
36        .then(|item| async {
37            let (key, row) = item?;
38
39            for Referencing {
40                table_name: referencing_table_name,
41                foreign_key:
42                    ForeignKey {
43                        referencing_column_name,
44                        referenced_column_name,
45                        on_delete,
46                        ..
47                    },
48            } in &referencings
49            {
50                let value = row
51                    .get_value(referenced_column_name)
52                    .ok_or(DeleteError::ValueNotFound(referenced_column_name.clone()))?
53                    .clone();
54
55                let expr = &Expr::BinaryOp {
56                    left: Box::new(Expr::Identifier(referencing_column_name.clone())),
57                    op: BinaryOperator::Eq,
58                    right: Box::new(Expr::try_from(value)?),
59                };
60
61                let columns = Some(Arc::from(Vec::new()));
62                let referencing_rows =
63                    fetch(storage, referencing_table_name, columns, Some(expr)).await?;
64
65                let referencing_row_exists = Box::pin(referencing_rows).next().await.is_some();
66                if referencing_row_exists && on_delete == &ReferentialAction::NoAction {
67                    return Err(DeleteError::ReferencingColumnExists(format!(
68                        "{referencing_table_name}.{referencing_column_name}"
69                    ))
70                    .into());
71                }
72            }
73
74            Ok::<_, Error>(key)
75        })
76        .try_collect::<Vec<_>>()
77        .await?;
78    let num_keys = keys.len();
79
80    storage
81        .delete_data(table_name, keys)
82        .await
83        .map(|_| Payload::Delete(num_keys))
84}