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