Skip to main content

rust_query/
scoped_transaction.rs

1use std::{
2    any::Any,
3    cell::{Cell, OnceCell},
4    marker::PhantomData,
5    ops::{Deref, DerefMut},
6};
7
8use crate::{
9    IntoExpr, Mutable, Table, TableRow, Transaction, private::IntoJoinable,
10    transaction::try_update_private, value::OptTable,
11};
12
13/// [Transaction] with mutation support and without downgrade support.
14///
15/// This type can be created using [Transaction::scope].
16pub struct TransactionScope<S: 'static> {
17    pub(crate) _p2: PhantomData<&'static Transaction<S>>,
18    pub(crate) tmp: Cell<Vec<Box<dyn Any>>>,
19}
20
21impl<S> DerefMut for TransactionScope<S> {
22    fn deref_mut(&mut self) -> &mut Self::Target {
23        self.tmp.take();
24        Transaction::new_ref()
25    }
26}
27
28impl<S> Deref for TransactionScope<S> {
29    type Target = Transaction<S>;
30
31    fn deref(&self) -> &Self::Target {
32        self.tmp.take();
33        Transaction::new_ref()
34    }
35}
36
37impl<S> TransactionScope<S> {
38    /// Retrieves a [Mutable] or `Option<Mutable>` from the database.
39    ///
40    /// The [Transaction] is borrowed mutably until the [Mutable] is dropped.
41    ///
42    /// ```
43    /// # #[rust_query::migration::schema(M)]
44    /// # pub mod vN {
45    /// #     pub struct Player {
46    /// #         #[unique]
47    /// #         pub number: i64,
48    /// #         pub name: String,
49    /// #         pub score: i64,
50    /// #     }
51    /// # }
52    /// # use v0::*;
53    /// # rust_query::Database::new(rust_query::migration::Config::open_in_memory()).transaction_mut_ok(|txn| {
54    /// txn.scope(|txn| {
55    /// let baz_id = txn.insert(Player {number: 1, name: "Baz".to_owned(), score: 0}).unwrap();
56    ///
57    /// let mut tmp = txn.mutable(baz_id);
58    /// tmp.score += 50;
59    /// tmp.name = format!("{}{}", tmp.name, tmp.score);
60    ///
61    /// if let Some(mut player) = txn.mutable(Player.number(1)) {
62    ///     player.score += 100;
63    /// }
64    /// # })});
65    /// ```
66    pub fn mutable<'t, T: OptTable<Schema = S>>(
67        &'t mut self,
68        val: impl IntoExpr<'static, S, Typ = T>,
69    ) -> T::Mutable<'t> {
70        let x = self.query_one(val.into_expr());
71        T::into_mutable(self, x)
72    }
73
74    /// Retrieve multiple [Mutable] rows from the database.
75    ///
76    /// Refer to [crate::args::Rows::join] for the kind of the parameter that is supported here.
77    /// This may be useful when you need mutable access to multiple rows (potentially at the same time).
78    ///
79    /// Getting a lazy [Iterator] over mutable rows instead of a [Vec] is not possible, because mutating
80    /// while iterating can result in duplicate rows.
81    ///
82    /// ```
83    /// # #[rust_query::migration::schema(M)]
84    /// # pub mod vN {
85    /// #     #[index(age)]
86    /// #     pub struct User { pub age: i64 }
87    /// # }
88    /// # use v0::*;
89    /// # rust_query::Database::new(rust_query::migration::Config::open_in_memory()).transaction_mut_ok(|mut txn| {
90    /// # txn.scope(|txn|{
91    /// # txn.insert_ok(User {age: 30});
92    /// for mut user in txn.mutable_vec(User.age(20)) {
93    ///     user.age += 1;
94    /// }
95    /// # })});
96    /// ```
97    pub fn mutable_vec<'t, T: Table<Schema = S>>(
98        &'t mut self,
99        val: impl IntoJoinable<'static, S, Typ = TableRow<T>>,
100    ) -> Vec<Mutable<'t, T>> {
101        let val = val.into_joinable();
102
103        let new_mutable = self.query(|rows| {
104            let val = rows.join(val);
105            rows.into_iter(val).map(|x| MutTemp::new(x) as _).collect()
106        });
107        self.tmp = Cell::new(new_mutable);
108
109        Cell::get_mut(&mut self.tmp)
110            .iter_mut()
111            .map(|x| Mutable::new(&mut **x))
112            .collect()
113    }
114}
115
116pub struct MutTemp<T: Table> {
117    pub inner: OnceCell<T::Mutable>,
118    pub row_id: TableRow<T>,
119}
120
121impl<T: Table> MutTemp<T> {
122    pub fn new(row_id: TableRow<T>) -> Box<Self> {
123        Box::new(MutTemp {
124            inner: OnceCell::new(),
125            row_id,
126        })
127    }
128}
129
130impl<T: Table> Drop for MutTemp<T> {
131    fn drop(&mut self) {
132        if let Some(update) = self.inner.take() {
133            let Ok(_) = try_update_private(self.row_id, update) else {
134                panic!("mutable can not fail, no unique is updated")
135            };
136        }
137    }
138}