rust_query/lazy.rs
1use std::{cell::OnceCell, ops::Deref};
2
3#[cfg(doc)]
4use crate::FromExpr;
5use crate::{Table, TableRow, Transaction, value::SecretFromSql};
6
7/// [Lazy] can be used to read any column of a table row and its parents.
8/// Columns are loaded on demand, one row at at time.
9///
10/// As an example, if you have two tables `Post` and `User`:
11/// ```
12/// # #[rust_query::migration::schema(Schema)]
13/// # pub mod vN {
14/// # pub struct Post {
15/// # pub author: User,
16/// # }
17/// # pub struct User {
18/// # pub name: String,
19/// # }
20/// # }
21/// # use rust_query::Lazy;
22/// # use v0::*;
23/// fn foo(post: Lazy<Post>) {
24/// let user = &post.author; // If the `post` row was not retrieved yet, then it is retrieved now to read the `user` column.
25/// let user_id = user.table_row(); // This doesn't access the database because the `user` id was already read from the `post` row.
26/// let user_name = &user.name; // If the `user` row was not retrieved yet, then it is retrieved now to read the `name` column.
27/// }
28/// ```
29///
30/// Note that [Lazy] borrows the transaction immutably.
31/// This means that it is not possible to keep a [Lazy] value when doing inserts or updates.
32/// Here are some alternatives to solve this problem:
33/// - [Copy]/[Clone] the columns that you need from the [Lazy] value before doing inserts and or updates.
34/// - Another option is to use [Lazy::table_row] to retrieve an owned [TableRow].
35/// This can then be used to create [crate::Expr] referencing the table columns for use in queries.
36/// - If you need many columns in a struct, then consider [derive@crate::FromExpr].
37pub struct Lazy<'transaction, T: Table> {
38 pub(crate) id: TableRow<T>,
39 pub(crate) lazy: OnceCell<Box<T::Lazy<'transaction>>>,
40 pub(crate) txn: &'transaction Transaction<T::Schema>,
41}
42
43impl<'transaction, T: Table> Lazy<'transaction, T> {
44 /// Get an owned [TableRow] out of this [Lazy] value.
45 ///
46 /// If you don't care about deleting the row then you probably want to
47 /// immediately use [TableRow::into_expr](crate::FromExpr::from_expr) on the returned [TableRow].
48 pub fn table_row(&self) -> TableRow<T> {
49 self.id
50 }
51}
52
53impl<'transaction, T: Table> Clone for Lazy<'transaction, T> {
54 fn clone(&self) -> Self {
55 Self {
56 id: self.id,
57 lazy: OnceCell::new(),
58 txn: self.txn,
59 }
60 }
61}
62
63impl<'transaction, T: Table> Deref for Lazy<'transaction, T> {
64 type Target = T::Lazy<'transaction>;
65
66 fn deref(&self) -> &Self::Target {
67 self.lazy
68 .get_or_init(|| Box::new(T::get_lazy(self.txn, self.id)))
69 }
70}
71
72impl<'transaction, T: Table> SecretFromSql for Lazy<'transaction, T> {
73 fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
74 Ok(Self {
75 id: TableRow::from_sql(value)?,
76 lazy: OnceCell::new(),
77 txn: Transaction::new_ref(),
78 })
79 }
80}