Skip to main content

persistant_relational/
backend.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use crate::{Dialect, RelError, Row, Value};
3use std::future::Future;
4use std::pin::Pin;
5use std::time::Duration;
6
7/// The isolation level a transaction asks for. A backend gives the level asked for or a stronger one;
8/// which it can actually show is what the `rel.isolation.*` leaves certify.
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
10#[non_exhaustive]
11pub enum Isolation {
12    /// No dirty reads: the backend's usual level for a transaction here.
13    #[default]
14    ReadCommitted,
15    /// A row or count read twice in the transaction reads the same.
16    RepeatableRead,
17    /// Concurrent transactions behave as if they ran one at a time (a conflicting one fails with
18    /// [`RelError::Serialization`], or waits).
19    Serializable,
20}
21
22/// A relational database, as small as it can be.
23///
24/// Statements are written in the backend's own [`Dialect`]; parameters are positional and written
25/// as that dialect writes them (`$1` for PostgreSQL). Nothing here translates SQL.
26pub trait RelationalBackend: Send + Sync {
27    /// A transaction on this backend. Dropping it without `commit` rolls it back.
28    type Tx<'a>: RelationalTx + 'a
29    where
30        Self: 'a;
31
32    /// The backend's name as it appears in matrix rows (`postgres`).
33    fn backend(&self) -> &str;
34
35    /// The server version string as it appears in matrix rows (`16.15`).
36    fn version(&self) -> &str;
37
38    /// The dialect statements must be written in.
39    fn dialect(&self) -> Dialect;
40
41    /// Run a statement that returns no rows; the result is the number of rows affected.
42    fn execute(
43        &self,
44        sql: &str,
45        params: &[Value],
46    ) -> impl Future<Output = Result<u64, RelError>> + Send;
47
48    /// Run a statement and return its rows.
49    fn query(
50        &self,
51        sql: &str,
52        params: &[Value],
53    ) -> impl Future<Output = Result<Vec<Row>, RelError>> + Send;
54
55    /// Begin a transaction at (at least) `isolation`.
56    fn begin(
57        &self,
58        isolation: Isolation,
59    ) -> impl Future<Output = Result<Self::Tx<'_>, RelError>> + Send;
60
61    /// The per-call timeout this backend enforces, if one is configured; `None` (the default) means a
62    /// call waits as long as the server does. Every call that takes longer returns
63    /// [`RelError::Timeout`].
64    fn call_bound(&self) -> Option<Duration> {
65        None
66    }
67
68    /// Test support for `kv.bounded_call`: take `lock_sql` (a statement that locks a row) in a
69    /// transaction of its own, drive `during` while that lock is held, then roll back. `during` is
70    /// dropped, unfinished, after `at_most`, so a broken bound cannot hang the test. A backend that
71    /// enforces no bound returns [`RelError::Unsupported`].
72    fn with_lock_held<'a>(
73        &'a self,
74        lock_sql: &'a str,
75        at_most: Duration,
76        during: Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
77    ) -> impl Future<Output = Result<(), RelError>> + Send {
78        let _ = (lock_sql, at_most, during);
79        async { Err(RelError::Unsupported("with_lock_held".to_string())) }
80    }
81}
82
83/// A transaction. Statements run inside it are committed or rolled back together.
84pub trait RelationalTx: Send {
85    /// As [`RelationalBackend::execute`], inside the transaction.
86    fn execute(
87        &mut self,
88        sql: &str,
89        params: &[Value],
90    ) -> impl Future<Output = Result<u64, RelError>> + Send;
91
92    /// As [`RelationalBackend::query`], inside the transaction.
93    fn query(
94        &mut self,
95        sql: &str,
96        params: &[Value],
97    ) -> impl Future<Output = Result<Vec<Row>, RelError>> + Send;
98
99    /// Commit.
100    fn commit(self) -> impl Future<Output = Result<(), RelError>> + Send;
101
102    /// Roll back.
103    fn rollback(self) -> impl Future<Output = Result<(), RelError>> + Send;
104}