Skip to main content

tank_core/
entity.rs

1use crate::{
2    ColumnDef, Context, Dataset, Driver, DynQuery, Executor, Expression, Query, QueryBuilder,
3    RawQuery, Result, Row, RowValues, RowsAffected, TableRef, future::Either, stream::Stream,
4    truncate_long, writer::SqlWriter,
5};
6use anyhow::anyhow;
7use futures::{FutureExt, StreamExt};
8use log::Level;
9use std::{
10    future::{self, Future},
11    pin::pin,
12    sync::Arc,
13};
14
15pub trait AsEntity {
16    type Entity: Entity;
17    fn as_entity(&self) -> &Self::Entity;
18}
19
20/// Database entity mapping.
21///
22/// Use `#[derive(Entity)]` to implement this trait.
23pub trait Entity: AsEntity + Expression {
24    /// Primary key type. A tuple of field types (or single type) forming the PK.
25    type PrimaryKey<'a>
26    where
27        Self: 'a;
28
29    /// Table reference matching the `#[tank(...)]` attributes.
30    fn table() -> &'static TableRef;
31
32    /// All column definitions in declaration order.
33    fn columns() -> &'static [ColumnDef];
34
35    /// Primary key column definitions. Empty if no PK defined.
36    fn primary_key_def() -> &'static [&'static ColumnDef];
37
38    /// Extract PK value(s) from `self`.
39    fn primary_key(&self) -> Self::PrimaryKey<'_>;
40
41    /// Build an expression matching the PK of `self`.
42    fn primary_key_expr(&self) -> impl Expression;
43
44    /// Unique constraint definitions.
45    fn unique_defs()
46    -> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &'static ColumnDef>>;
47
48    /// Full row representation including all persisted columns.
49    fn row_values(&self) -> RowValues;
50
51    /// Full row representation with column labels.
52    fn row(&self) -> Row {
53        Row {
54            labels: Self::columns()
55                .into_iter()
56                .map(|v| v.name().to_string())
57                .collect::<Arc<[String]>>(),
58            values: self.row_values(),
59        }
60    }
61
62    /// Reconstruct `Self` from a labeled row.
63    ///
64    /// Fails if columns are missing or type conversion fails.
65    fn from_row(row: Row) -> Result<Self>
66    where
67        Self: Sized;
68
69    /// Create table (and optional schema).
70    ///
71    /// - `if_not_exists`: Emits `IF NOT EXISTS` if supported.
72    /// - `create_schema`: Attempts schema creation first.
73    fn create_table(
74        executor: &mut impl Executor,
75        if_not_exists: bool,
76        create_schema: bool,
77    ) -> impl Future<Output = Result<()>> + Send
78    where
79        Self: Sized,
80    {
81        async move {
82            let mut query = DynQuery::with_capacity(2048);
83            let writer = executor.driver().sql_writer();
84            if create_schema && !Self::table().schema.is_empty() {
85                writer.write_create_schema::<Self>(&mut query, true);
86            }
87            if !executor.accepts_multiple_statements() && !query.is_empty() {
88                let mut q = query.into_query(executor.driver());
89                executor.execute(&mut q).await?;
90                // To reuse the allocated buffer
91                query = q.into();
92                query.clear();
93            }
94            writer.write_create_table::<Self>(&mut query, if_not_exists);
95            executor.execute(query).await.map(|_| ())
96        }
97    }
98
99    /// Drop the table (and optional schema).
100    ///
101    /// - `if_exists`: Emits `IF EXISTS` if supported.
102    /// - `drop_schema`: Drops schema *after* table removal (if empty).
103    fn drop_table(
104        executor: &mut impl Executor,
105        if_exists: bool,
106        drop_schema: bool,
107    ) -> impl Future<Output = Result<()>> + Send
108    where
109        Self: Sized,
110    {
111        async move {
112            let mut query = DynQuery::with_capacity(256);
113            let writer = executor.driver().sql_writer();
114            writer.write_drop_table::<Self>(&mut query, if_exists);
115            if drop_schema && !Self::table().schema.is_empty() {
116                if !executor.accepts_multiple_statements() {
117                    let mut q = query.into_query(executor.driver());
118                    executor.execute(&mut q).await?;
119                    // To reuse the allocated buffer
120                    query = q.into();
121                    query.clear();
122                }
123                writer.write_drop_schema::<Self>(&mut query, true);
124            }
125            executor.execute(query).await.map(|_| ())
126        }
127    }
128
129    /// Insert a single entity.
130    fn insert_one(
131        executor: &mut impl Executor,
132        entity: impl AsEntity,
133    ) -> impl Future<Output = Result<RowsAffected>> + Send {
134        let mut query = DynQuery::with_capacity(128);
135        executor
136            .driver()
137            .sql_writer()
138            .write_insert(&mut query, [entity], false);
139        executor.execute(query)
140    }
141
142    /// Bulk insert entities.
143    fn insert_many<It>(
144        executor: &mut impl Executor,
145        items: It,
146    ) -> impl Future<Output = Result<RowsAffected>> + Send
147    where
148        Self: Sized,
149        It: IntoIterator + Send,
150        It::IntoIter: Send,
151        It::Item: AsEntity,
152    {
153        executor.append(items)
154    }
155
156    /// Prepare (but do not yet run) a SQL select query.
157    ///
158    /// Returns the prepared statement.
159    fn prepare_find<Exec: Executor>(
160        executor: &mut Exec,
161        condition: impl Expression,
162        limit: Option<u32>,
163    ) -> impl Future<Output = Result<Query<Exec::Driver>>> + Send {
164        let builder = QueryBuilder::new()
165            .select(Self::columns())
166            .from(Self::table())
167            .where_expr(condition)
168            .limit(limit);
169        let writer = executor.driver().sql_writer();
170        let mut query = DynQuery::default();
171        writer.write_select(&mut query, &builder);
172        async {
173            if let DynQuery::Raw(RawQuery(sql)) = query {
174                executor.prepare(sql).await
175            } else {
176                Ok(query.into())
177            }
178        }
179    }
180
181    /// Finds the first entity matching a condition expression.
182    ///
183    /// Returns `Ok(None)` if no row matches.
184    fn find_one(
185        executor: &mut impl Executor,
186        condition: impl Expression,
187    ) -> impl Future<Output = Result<Option<Self>>> + Send
188    where
189        Self: Sized,
190    {
191        let stream = Self::find_many(executor, condition, Some(1));
192        async move { pin!(stream).into_future().map(|(v, _)| v).await.transpose() }
193    }
194
195    /// Streams entities matching a condition.
196    ///
197    /// `limit` restricts the maximum number of rows returned at a database level if `Some`
198    /// (if supported by the driver, unlimited otherwise).
199    fn find_many(
200        executor: &mut impl Executor,
201        condition: impl Expression,
202        limit: Option<u32>,
203    ) -> impl Stream<Item = Result<Self>> + Send
204    where
205        Self: Sized,
206    {
207        let builder = QueryBuilder::new()
208            .select(Self::columns())
209            .from(Self::table())
210            .where_expr(condition)
211            .limit(limit);
212        executor
213            .fetch(builder.build(&executor.driver()))
214            .map(|result| result.and_then(Self::from_row))
215    }
216
217    /// Deletes all entities matching a condition.
218    ///
219    /// Returns the number of deleted rows.
220    fn delete_many(
221        executor: &mut impl Executor,
222        condition: impl Expression,
223    ) -> impl Future<Output = Result<RowsAffected>> + Send
224    where
225        Self: Sized,
226    {
227        let mut query = DynQuery::with_capacity(128);
228        executor
229            .driver()
230            .sql_writer()
231            .write_delete::<Self>(&mut query, condition);
232        executor.execute(query)
233    }
234
235    /// Saves the entity (insert or update if available) based on primary key presence.
236    ///
237    /// Errors:
238    /// - Missing PK in the table.
239    /// - Execution failures from underlying driver.
240    fn save(&self, executor: &mut impl Executor) -> impl Future<Output = Result<()>> + Send
241    where
242        Self: Sized,
243        for<'s> &'s Self: AsEntity,
244    {
245        if Self::primary_key_def().is_empty() {
246            let error = anyhow!(
247                "Cannot save an entity without a primary key, it would always result in an insert",
248            );
249            log::error!("{error:#}");
250            return Either::Left(future::ready(Err(error)));
251        }
252        let mut query = DynQuery::with_capacity(512);
253        executor
254            .driver()
255            .sql_writer()
256            .write_insert(&mut query, [self], true);
257        let sql = query.as_str();
258        let context = format!("While saving using the query {}", truncate_long!(sql));
259        Either::Right(executor.execute(query).map(|mut v| {
260            if let Ok(result) = v
261                && let Some(affected) = result.rows_affected
262                && affected > 2
263            {
264                v = Err(anyhow!(
265                    "The driver returned affected rows: {affected} (expected <= 2)"
266                ));
267            }
268            match v {
269                Ok(_) => Ok(()),
270                Err(e) => Err(e.context(context)),
271            }
272        }))
273    }
274
275    /// Deletes this entity instance via its primary key.
276    ///
277    /// Errors:
278    /// - Missing PK in the table.
279    /// - If not exactly one row was deleted.
280    /// - Execution failures from underlying driver.
281    fn delete(&self, executor: &mut impl Executor) -> impl Future<Output = Result<()>> + Send
282    where
283        Self: Sized,
284    {
285        if Self::primary_key_def().is_empty() {
286            let error =
287                anyhow!("Cannot delete an entity without a primary key, it would delete nothing",);
288            log::error!("{error:#}");
289            return Either::Left(future::ready(Err(error)));
290        }
291        Either::Right(
292            Self::delete_many(executor, self.primary_key_expr()).map(|v| {
293                v.and_then(|v| {
294                    if let Some(affected) = v.rows_affected {
295                        if affected != 1 {
296                            let error = anyhow!(
297                                "The query deleted {affected} rows instead of the expected 1"
298                            );
299                            log::log!(
300                                if affected == 0 {
301                                    Level::Info
302                                } else {
303                                    Level::Error
304                                },
305                                "{error}",
306                            );
307                            return Err(error);
308                        }
309                    }
310                    Ok(())
311                })
312            }),
313        )
314    }
315}
316
317impl<E: Entity> Dataset for E {
318    /// Indicates whether column names should be fully qualified with schema and table name.
319    ///
320    /// For entities this returns `false` to keep queries concise, for joins it returns `true`.
321    fn qualified_columns() -> bool
322    where
323        Self: Sized,
324    {
325        false
326    }
327
328    /// Writes the table reference into the out string.
329    fn write_table_name(&self, writer: &dyn SqlWriter, context: &mut Context, out: &mut DynQuery) {
330        Self::table().write_table_name(writer, context, out);
331    }
332
333    fn table_ref(&self) -> TableRef {
334        Self::table().clone()
335    }
336}