Skip to main content

coven/
handle_writes.rs

1//! Writes: the app's SQL and coven's steps as one write, and the app bytes
2//! staged before one (§4, §17.2, E3).
3use crate::coven_write::CovenWrite;
4use crate::*;
5
6impl CovenHandle {
7    /// Runs one write: the app's SQL and coven's steps, in one transaction.
8    /// It fails with the closure's own error type, which keeps a failed
9    /// rollback or byte removal beside the write's failure
10    /// (`WriteFailure`).
11    pub async fn write<F, R, E>(&self, write: F) -> Result<R, E>
12    where
13        F: FnOnce(CovenWrite<'_>) -> Result<R, E> + Send + 'static,
14        R: Send + 'static,
15        E: WriteFailure + Send + 'static,
16    {
17        let (steps, files) = (self.steps.clone(), self.files.clone());
18        self.database.write_as(move |sql| write(CovenWrite::new(sql, &steps, &files))).await
19    }
20
21    /// Runs one bulk write: SQL that changes device-local tables alone, run
22    /// as plain SQLite with no record of each row change, so emptying a table
23    /// takes one step and filling one runs at SQLite's own speed (§4). A
24    /// statement that would write a synced table, itself or through a
25    /// trigger or foreign-key action, fails the write with
26    /// `SyncedTableInBulkWrite`. Triggers still run. After commit, every live
27    /// query that read a table the write's statements may change runs again,
28    /// whatever rows it read; a small change that should rerun only the live
29    /// queries reading its rows belongs in `write`.
30    pub async fn write_bulk<F, R, E>(&self, sql: F) -> Result<R, E>
31    where
32        F: FnOnce(BulkSqlContext<'_>) -> Result<R, E> + Send + 'static,
33        R: Send + 'static,
34        E: WriteFailure + Send + 'static,
35    {
36        self.database.write_bulk(sql).await
37    }
38
39    /// Stages app bytes for a write that puts them in rows (§17.2): `stage`
40    /// stages each file and returns what that write needs, such as the ids
41    /// staging gave them, and the write takes it with
42    /// `CovenWrite::put_staged`. The bytes are streamed to coven's own copies,
43    /// synced and flushed before this returns; close waits for staging and
44    /// its cleanup, including cancellation. The staged files hold nothing
45    /// close waits for: dropped uncommitted, or after the store closes, they
46    /// are removed by the next write or open. An undeclared namespace fails
47    /// with `UndeclaredNamespace`, a user one with `NamespaceKind`.
48    pub async fn stage_files<F, T, E>(&self, stage: F) -> Result<StagedFiles<T>, E>
49    where
50        F: FnOnce(&mut WriteBatch<'_>) -> Result<T, E> + Send + 'static,
51        T: Send + 'static,
52        E: WriteFailure + Send + 'static,
53    {
54        self.database.stage_files(stage).await
55    }
56}
57
58#[cfg(test)]
59impl CovenHandle {
60    /// Stage app bytes, then run a write that puts them in its rows, as an
61    /// app does with `stage_files` and `put_staged`, for tests.
62    pub(crate) async fn test_staged_write<F, S, T, R, E>(&self, stage: F, write: S) -> Result<R, E>
63    where
64        F: FnOnce(&mut WriteBatch<'_>) -> Result<T, E> + Send + 'static,
65        S: FnOnce(CovenWrite<'_>, T) -> Result<R, E> + Send + 'static,
66        T: Send + 'static,
67        R: Send + 'static,
68        E: WriteFailure + Send + 'static,
69    {
70        let staged = self.stage_files(stage).await?;
71        self.write(move |sql| {
72            let value = sql.put_staged(staged)?;
73            write(sql, value)
74        })
75        .await
76    }
77}
78
79#[cfg(test)]
80#[path = "handle_writes_tests.rs"]
81mod tests;