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}