cuprate_database/
database.rs

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Abstracted database table operations; `trait DatabaseRo` & `trait DatabaseRw`.

//---------------------------------------------------------------------------------------------------- Import
use std::ops::RangeBounds;

use crate::{error::RuntimeError, table::Table};

//---------------------------------------------------------------------------------------------------- DatabaseIter
/// Generic post-fix documentation for `DatabaseIter` methods.
macro_rules! doc_iter {
    () => {
        r"Although the returned iterator itself is tied to the lifetime
of `&self`, the returned values from the iterator are _owned_.

# Errors
The construction of the iterator itself may error.

Each iteration of the iterator has the potential to error as well."
    };
}

/// Database (key-value store) read-only iteration abstraction.
///
/// These are read-only iteration-related operations that
/// can only be called from [`DatabaseRo`] objects.
///
/// # Hack
/// This is a HACK to get around the fact [`DatabaseRw`] tables
/// cannot safely return values returning lifetimes, as such,
/// only read-only tables implement this trait.
///
/// - <https://github.com/Cuprate/cuprate/pull/102#discussion_r1548695610>
/// - <https://github.com/Cuprate/cuprate/pull/104>
pub trait DatabaseIter<T: Table> {
    /// Get an [`Iterator`] of value's corresponding to a range of keys.
    ///
    /// For example:
    /// ```rust,ignore
    /// // This will return all 100 values corresponding
    /// // to the keys `{0, 1, 2, ..., 100}`.
    /// self.get_range(0..100);
    /// ```
    ///
    /// Although the returned iterator itself is tied to the lifetime
    /// of `&'a self`, the returned values from the iterator are _owned_.
    ///
    #[doc = doc_iter!()]
    fn get_range<'a, Range>(
        &'a self,
        range: Range,
    ) -> Result<impl Iterator<Item = Result<T::Value, RuntimeError>> + 'a, RuntimeError>
    where
        Range: RangeBounds<T::Key> + 'a;

    /// Get an [`Iterator`] that returns the `(key, value)` types for this database.
    #[doc = doc_iter!()]
    #[expect(clippy::iter_not_returning_iterator)]
    fn iter(
        &self,
    ) -> Result<impl Iterator<Item = Result<(T::Key, T::Value), RuntimeError>> + '_, RuntimeError>;

    /// Get an [`Iterator`] that returns _only_ the `key` type for this database.
    #[doc = doc_iter!()]
    fn keys(&self)
        -> Result<impl Iterator<Item = Result<T::Key, RuntimeError>> + '_, RuntimeError>;

    /// Get an [`Iterator`] that returns _only_ the `value` type for this database.
    #[doc = doc_iter!()]
    fn values(
        &self,
    ) -> Result<impl Iterator<Item = Result<T::Value, RuntimeError>> + '_, RuntimeError>;
}

//---------------------------------------------------------------------------------------------------- DatabaseRo
/// Generic post-fix documentation for `DatabaseR{o,w}` methods.
macro_rules! doc_database {
    () => {
        r"# Errors
This will return [`RuntimeError::KeyNotFound`] if:
- Input does not exist OR
- Database is empty"
    };
}

/// Database (key-value store) read abstraction.
///
/// This is a read-only database table,
/// write operations are defined in [`DatabaseRw`].
///
/// # Safety
/// The table type that implements this MUST be `Send`.
///
/// However if the table holds a reference to a transaction:
/// - only the transaction only has to be `Send`
/// - the table cannot implement `Send`
///
/// For example:
///
/// `heed`'s transactions are `Send` but `HeedTableRo` contains a `&`
/// to the transaction, as such, if `Send` were implemented on `HeedTableRo`
/// then 1 transaction could be used to open multiple tables, then sent to
/// other threads - this would be a soundness hole against `HeedTableRo`.
///
/// `&T` is only `Send` if `T: Sync`.
///
/// `heed::RoTxn: !Sync`, therefore our table
/// holding `&heed::RoTxn` must NOT be `Send`.
///
/// - <https://doc.rust-lang.org/std/marker/trait.Sync.html>
/// - <https://doc.rust-lang.org/nomicon/send-and-sync.html>
pub unsafe trait DatabaseRo<T: Table> {
    /// Get the value corresponding to a key.
    #[doc = doc_database!()]
    fn get(&self, key: &T::Key) -> Result<T::Value, RuntimeError>;

    /// Returns `true` if the database contains a value for the specified key.
    ///
    /// # Errors
    /// Note that this will _never_ return `Err(RuntimeError::KeyNotFound)`,
    /// as in that case, `Ok(false)` will be returned.
    ///
    /// Other errors may still occur.
    fn contains(&self, key: &T::Key) -> Result<bool, RuntimeError> {
        match self.get(key) {
            Ok(_) => Ok(true),
            Err(RuntimeError::KeyNotFound) => Ok(false),
            Err(e) => Err(e),
        }
    }

    /// Returns the number of `(key, value)` pairs in the database.
    ///
    /// # Errors
    /// This will never return [`RuntimeError::KeyNotFound`].
    fn len(&self) -> Result<u64, RuntimeError>;

    /// Returns the first `(key, value)` pair in the database.
    #[doc = doc_database!()]
    fn first(&self) -> Result<(T::Key, T::Value), RuntimeError>;

    /// Returns the last `(key, value)` pair in the database.
    #[doc = doc_database!()]
    fn last(&self) -> Result<(T::Key, T::Value), RuntimeError>;

    /// Returns `true` if the database contains no `(key, value)` pairs.
    ///
    /// # Errors
    /// This can only return [`RuntimeError::Io`] on errors.
    fn is_empty(&self) -> Result<bool, RuntimeError>;
}

//---------------------------------------------------------------------------------------------------- DatabaseRw
/// Database (key-value store) read/write abstraction.
///
/// All [`DatabaseRo`] functions are also callable by [`DatabaseRw`].
pub trait DatabaseRw<T: Table>: DatabaseRo<T> {
    /// Insert a key-value pair into the database.
    ///
    /// This will overwrite any existing key-value pairs.
    ///
    #[doc = doc_database!()]
    ///
    /// This will never [`RuntimeError::KeyExists`].
    fn put(&mut self, key: &T::Key, value: &T::Value) -> Result<(), RuntimeError>;

    /// Delete a key-value pair in the database.
    ///
    /// This will return `Ok(())` if the key does not exist.
    ///
    #[doc = doc_database!()]
    ///
    /// This will never [`RuntimeError::KeyExists`].
    fn delete(&mut self, key: &T::Key) -> Result<(), RuntimeError>;

    /// Delete and return a key-value pair in the database.
    ///
    /// This is the same as [`DatabaseRw::delete`], however,
    /// it will serialize the `T::Value` and return it.
    ///
    #[doc = doc_database!()]
    fn take(&mut self, key: &T::Key) -> Result<T::Value, RuntimeError>;

    /// Fetch the value, and apply a function to it - or delete the entry.
    ///
    /// This will call [`DatabaseRo::get`] and call your provided function `f` on it.
    ///
    /// The [`Option`] `f` returns will dictate whether `update()`:
    /// - Updates the current value OR
    /// - Deletes the `(key, value)` pair
    ///
    /// - If `f` returns `Some(value)`, that will be [`DatabaseRw::put`] as the new value
    /// - If `f` returns `None`, the entry will be [`DatabaseRw::delete`]d
    ///
    #[doc = doc_database!()]
    fn update<F>(&mut self, key: &T::Key, mut f: F) -> Result<(), RuntimeError>
    where
        F: FnMut(T::Value) -> Option<T::Value>,
    {
        let value = DatabaseRo::get(self, key)?;

        match f(value) {
            Some(value) => DatabaseRw::put(self, key, &value),
            None => DatabaseRw::delete(self, key),
        }
    }

    /// Removes and returns the first `(key, value)` pair in the database.
    ///
    #[doc = doc_database!()]
    fn pop_first(&mut self) -> Result<(T::Key, T::Value), RuntimeError>;

    /// Removes and returns the last `(key, value)` pair in the database.
    ///
    #[doc = doc_database!()]
    fn pop_last(&mut self) -> Result<(T::Key, T::Value), RuntimeError>;
}