1use super::Database;
4use crate::{DbError, NewOperation, OperationId, OperationRecord, OperationUpdate};
5use coven_foundation::id_source::DeviceId;
6use std::collections::BTreeSet;
7
8impl Database {
9 pub async fn operations(&self) -> Result<Vec<OperationRecord>, DbError> {
11 self.call(move |inner| inner.access.with_committed(crate::operation::read)).await
12 }
13
14 pub async fn operation(&self, id: OperationId) -> Result<Option<OperationRecord>, DbError> {
17 self.call(move |inner| {
18 inner.access.with_committed(|writer| crate::operation::find(writer, id))
19 })
20 .await
21 }
22
23 pub async fn first_operation(&self, kind: &str) -> Result<Option<OperationRecord>, DbError> {
25 let kind = kind.to_owned();
26 self.call(move |inner| {
27 inner.access.with_committed(|writer| crate::operation::first(writer, &kind))
28 })
29 .await
30 }
31
32 pub async fn start_operation(&self, operation: NewOperation) -> Result<OperationId, DbError> {
34 self.call(move |inner| {
35 inner.access.with_writer(|writer| {
36 writer.transaction(|db| crate::operation::insert(db, &operation))
37 })
38 })
39 .await
40 }
41
42 pub async fn start_listed_operation(
46 &self,
47 operation: NewOperation,
48 kind: String,
49 ) -> Result<OperationId, DbError> {
50 self.call(move |inner| {
51 inner.access.with_writer(|writer| {
52 writer.transaction(|db| crate::operation::insert_for_app(db, &operation, &kind))
53 })
54 })
55 .await
56 }
57
58 pub async fn start_operation_advancing<E: crate::WriteFailure + Send + 'static>(
63 &self,
64 operation: NewOperation,
65 update: impl FnOnce(OperationId) -> Result<OperationUpdate, E> + Send + 'static,
66 ) -> Result<OperationId, E> {
67 self.call(move |inner| {
68 inner.access.with_writer(|writer| {
69 writer.transaction_as(|db| {
70 let id = crate::operation::insert(db, &operation)?;
71 crate::operation::advance(db, &update(id)?)?;
72 Ok(id)
73 })
74 })
75 })
76 .await
77 }
78
79 pub async fn reserve_snapshot_number(&self) -> Result<std::num::NonZeroU64, DbError> {
83 self.call(move |inner| {
84 inner
85 .access
86 .with_writer(|writer| writer.transaction(crate::operation::reserve_snapshot))
87 })
88 .await
89 }
90
91 pub async fn advance_operation(&self, update: OperationUpdate) -> Result<(), DbError> {
93 self.call(move |inner| {
94 inner.access.with_writer(|writer| {
95 writer.transaction(|db| crate::operation::advance(db, &update))
96 })
97 })
98 .await
99 }
100
101 pub async fn advance_operations<T: Send + 'static>(
108 &self,
109 kind: &str,
110 step: impl FnOnce(&[OperationRecord]) -> Result<(Vec<OperationUpdate>, T), DbError>
111 + Send
112 + 'static,
113 ) -> Result<T, DbError> {
114 let kind = kind.to_owned();
115 self.call(move |inner| {
116 inner.access.with_writer(|writer| {
117 writer.state_transaction(|db| {
118 let (updates, concluded) = step(&crate::operation::of_kind(db, &kind)?)?;
119 for update in &updates {
120 crate::operation::advance(db, update)?;
121 }
122 Ok(concluded)
123 })
124 })
125 })
126 .await
127 }
128
129 pub async fn hold_member_keys(
133 &self,
134 keys: Vec<coven_crypto::MemberPublicKeys>,
135 ) -> Result<(), DbError> {
136 self.call(move |inner| {
137 inner.access.with_writer(|writer| {
138 writer.transaction(|db| crate::operation::hold_keys(db, &keys))
139 })
140 })
141 .await
142 }
143
144 pub async fn operation_pending<P: crate::PendingValue>(
146 &self,
147 id: OperationId,
148 ) -> Result<Option<P>, DbError> {
149 self.call(move |inner| inner.access.with_writer(|db| crate::pending::operation(db, id)))
150 .await
151 }
152
153 #[cfg(test)]
155 pub async fn retry_operation(&self, id: OperationId) -> Result<(), DbError> {
156 self.call(move |inner| {
157 inner.access.with_writer(|writer| {
158 writer.transaction(|db| crate::pending::clear_operation(db, id))
159 })
160 })
161 .await
162 }
163
164 #[cfg(test)]
166 pub async fn advance_operation_pending<P: crate::PendingValue>(
167 &self,
168 update: OperationUpdate,
169 pending: P,
170 ) -> Result<(), DbError> {
171 self.call(move |inner| {
172 inner.access.with_writer(|writer| {
173 if pending.operation() != Some(update.id) {
174 return Err(DbError::Invariant {
175 what: "an operation's condition names its journal row",
176 });
177 }
178 writer.transaction(|db| {
179 crate::operation::advance(db, &update)?;
180 crate::pending::save(db, &pending)
181 })
182 })
183 })
184 .await
185 }
186
187 pub async fn finish_operation_with_outcome(
192 &self,
193 id: OperationId,
194 outcome: crate::OperationOutcome,
195 ) -> Result<(), DbError> {
196 self.call(move |inner| {
197 inner.access.with_writer(|writer| {
198 writer.transaction(|db| crate::operation::finish_with_outcome(db, id, &outcome))
199 })
200 })
201 .await
202 }
203
204 pub async fn finish_operation_with_removal(
211 &self,
212 id: OperationId,
213 children: Vec<OperationId>,
214 removal: crate::ObjectRemoval,
215 outcome: Option<(String, crate::OperationOutcome)>,
216 ) -> Result<(), DbError> {
217 self.call(move |inner| {
218 inner.access.with_writer(|writer| {
219 writer.transaction(|db| {
220 if let Some((kind, outcome)) = &outcome {
224 crate::operation::list_outcome(db, id, kind, outcome)?;
225 }
226 for id in children.iter().copied().chain([id]) {
227 if crate::operation::finish(db, id)? != 1 {
228 return Err(DbError::OperationChanged(id));
229 }
230 }
231 crate::object_removals::record(db, &removal)
232 })
233 })
234 })
235 .await
236 }
237
238 pub async fn finish_operation_advancing(
245 &self,
246 child: OperationId,
247 outcome: crate::OperationOutcome,
248 parent: OperationUpdate,
249 ) -> Result<(), DbError> {
250 self.call(move |inner| {
251 inner.access.with_writer(|writer| {
252 writer.transaction(|db| {
253 crate::operation::advance(db, &parent)?;
254 crate::operation::finish_with_outcome(db, child, &outcome)
255 })
256 })
257 })
258 .await
259 }
260
261 pub async fn end_for_good(&self, stop: crate::DeviceStop) -> Result<(), DbError> {
267 self.call(move |inner| {
268 inner.access.with_writer(|writer| {
269 writer.state_transaction(|db| crate::operation::end_for_good(db, stop))
270 })
271 })
272 .await
273 }
274
275 pub async fn device_stop(&self) -> Result<Option<crate::DeviceStop>, DbError> {
281 self.call(|inner| inner.access.with_committed(crate::operation::stopped)).await
282 }
283
284 pub async fn due_object_removals(&self, now: u64) -> Result<Vec<String>, DbError> {
287 let now = i64::try_from(now).unwrap_or(i64::MAX);
288 self.call(move |inner| {
289 inner.access.with_committed(|db| {
290 db.query(
291 "SELECT path FROM _coven_object_removals WHERE after<=?1 ORDER BY after",
292 [now],
293 |r| r.get(0),
294 )
295 })
296 })
297 .await
298 }
299
300 pub async fn object_removed(&self, path: String) -> Result<(), DbError> {
302 self.call(move |inner| {
303 inner.access.with_writer(|writer| {
304 writer.transaction(|db| crate::object_removals::removed(db, &path))
305 })
306 })
307 .await
308 }
309
310 pub async fn requests_ended(&self) -> Result<BTreeSet<DeviceId>, DbError> {
313 self.call(move |inner| {
314 inner.access.with_committed(|db| {
315 db.query("SELECT device FROM _coven_requests_ended", [], |r| {
316 r.get::<_, DeviceId>(0)
317 })
318 })
319 })
320 .await
321 .map(|devices| devices.into_iter().collect())
322 }
323
324 pub async fn record_requests_ended(&self, device: DeviceId) -> Result<(), DbError> {
327 self.call(move |inner| {
328 inner.access.with_writer(|writer| {
329 writer.transaction(|db| {
330 db.internal_execute(
331 "INSERT INTO _coven_requests_ended(device) VALUES(?1)",
332 [device],
333 )
334 .map(drop)
335 })
336 })
337 })
338 .await
339 }
340
341 pub async fn gone_storage_cleared(&self) -> Result<BTreeSet<DeviceId>, DbError> {
344 self.call(move |inner| {
345 inner.access.with_committed(|db| {
346 db.query("SELECT device FROM _coven_gone_storage_cleared", [], |r| {
347 r.get::<_, DeviceId>(0)
348 })
349 })
350 })
351 .await
352 .map(|devices| devices.into_iter().collect())
353 }
354
355 pub async fn record_gone_storage_cleared(&self, device: DeviceId) -> Result<(), DbError> {
360 self.call(move |inner| {
361 inner.access.with_writer(|writer| {
362 writer.transaction(|db| {
363 db.internal_execute(
364 "INSERT INTO _coven_gone_storage_cleared(device) VALUES(?1) ON CONFLICT DO NOTHING",
365 [device],
366 )
367 .map(drop)
368 })
369 })
370 })
371 .await
372 }
373
374 pub async fn finish_operation(&self, id: OperationId) -> Result<(), DbError> {
377 self.call(move |inner| {
378 inner.access.with_writer(|writer| {
379 writer.transaction(|db| crate::operation::finish(db, id).map(drop))
380 })
381 })
382 .await
383 }
384}