Skip to main content

turso_orm/query/
delete.rs

1//! The `DELETE` builders, modeled by [`DeleteOne`] and [`DeleteMany`].
2//!
3//! [`DeleteOne`] is a thin layer over [`DeleteMany`]: it only contributes the
4//! primary-key condition read from an active model, so that both paths
5//! render through the same statement and report the same
6//! [`DeleteResult`]. Both offer `exec_with_returning`, which uses
7//! `DELETE ... RETURNING *` to hand back the rows as they were just before
8//! removal.
9
10use std::marker::PhantomData;
11
12use turso_orm_driver::ConnectionTrait;
13use turso_sql::{Build, IntoCondition, Returning, Statement, Value};
14
15use crate::entity::{ActiveModelTrait, EntityTrait, FromQueryResult};
16use crate::query::select::pk_condition;
17use crate::{DbErr, Result};
18
19/// The outcome of a delete.
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub struct DeleteResult {
22    /// The number of rows deleted.
23    pub rows_affected: u64,
24}
25
26/// A `DELETE` of one active model, matched by primary key.
27#[derive(Clone, Debug)]
28pub struct DeleteOne<A: ActiveModelTrait> {
29    /// The active model whose primary key selects the row.
30    model: A,
31}
32
33impl<A: ActiveModelTrait> DeleteOne<A> {
34    /// Wraps the active model to delete.
35    pub(crate) fn new(model: A) -> Self {
36        Self { model }
37    }
38
39    /// Executes the delete.
40    ///
41    /// # Errors
42    ///
43    /// Returns [`DbErr::PrimaryKeyNotSet`] when a key attribute of the model
44    /// is `NotSet`; [`DbErr::Driver`] when the statement fails.
45    pub async fn exec<C: ConnectionTrait>(self, db: &C) -> Result<DeleteResult> {
46        let pk = self
47            .model
48            .get_primary_key_value()
49            .ok_or(DbErr::PrimaryKeyNotSet)?;
50        DeleteMany::<A::Entity>::new()
51            .filter_by_pk(pk)
52            .exec(db)
53            .await
54    }
55
56    /// Executes the delete with `RETURNING *` and returns the removed row.
57    ///
58    /// # Errors
59    ///
60    /// Returns [`DbErr::PrimaryKeyNotSet`] when a key attribute of the model
61    /// is `NotSet`; [`DbErr::RecordNotFound`] when no row matched;
62    /// [`DbErr::Driver`] when the statement fails or the returned row cannot
63    /// be decoded.
64    pub async fn exec_with_returning<C: ConnectionTrait>(
65        self,
66        db: &C,
67    ) -> Result<<A::Entity as EntityTrait>::Model> {
68        let pk = self
69            .model
70            .get_primary_key_value()
71            .ok_or(DbErr::PrimaryKeyNotSet)?;
72        DeleteMany::<A::Entity>::new()
73            .filter_by_pk(pk)
74            .exec_with_returning(db)
75            .await?
76            .into_iter()
77            .next()
78            .ok_or_else(|| DbErr::RecordNotFound("delete".into()))
79    }
80}
81
82/// A `DELETE` over any number of rows of an entity.
83#[derive(Clone, Debug)]
84pub struct DeleteMany<E: EntityTrait> {
85    /// The statement being assembled.
86    query: turso_sql::Delete,
87    /// Ties the builder to its entity without storing it.
88    _e: PhantomData<E>,
89}
90
91impl<E: EntityTrait> DeleteMany<E> {
92    /// Starts `DELETE FROM table` with no condition.
93    pub(crate) fn new() -> Self {
94        Self {
95            query: turso_sql::Query::delete().from_table(E::TABLE_NAME),
96            _e: PhantomData,
97        }
98    }
99
100    /// Restricts the delete to the row whose primary key equals `values`.
101    pub(crate) fn filter_by_pk(mut self, values: Vec<Value>) -> Self {
102        self.query = self.query.and_where(pk_condition::<E>(values));
103        self
104    }
105
106    /// Adds a `WHERE` condition, `AND`ed with the previous ones.
107    #[must_use]
108    pub fn filter(mut self, cond: impl IntoCondition) -> Self {
109        self.query = self.query.and_where(cond);
110        self
111    }
112
113    /// Renders the statement.
114    pub fn build(&self) -> Statement {
115        self.query.to_statement()
116    }
117
118    /// Executes the delete.
119    ///
120    /// # Errors
121    ///
122    /// Returns [`DbErr::Driver`] when the statement fails.
123    pub async fn exec<C: ConnectionTrait>(self, db: &C) -> Result<DeleteResult> {
124        let result = db.execute(self.build()).await?;
125        Ok(DeleteResult {
126            rows_affected: result.rows_affected,
127        })
128    }
129
130    /// Executes the delete with `RETURNING *` and decodes the removed rows.
131    ///
132    /// # Errors
133    ///
134    /// Returns [`DbErr::Driver`] when the statement fails or a returned row
135    /// cannot be decoded.
136    pub async fn exec_with_returning<C: ConnectionTrait>(
137        mut self,
138        db: &C,
139    ) -> Result<Vec<E::Model>> {
140        self.query = self.query.returning(Returning::All);
141        db.query_all(self.build())
142            .await?
143            .iter()
144            .map(|r| E::Model::from_query_result(r, ""))
145            .collect()
146    }
147}