heed/
cursor.rs

1use std::ops::{Deref, DerefMut};
2use std::{marker, mem, ptr};
3
4use crate::mdb::error::mdb_result;
5use crate::mdb::ffi;
6use crate::*;
7
8pub struct RoCursor<'txn> {
9    cursor: *mut ffi::MDB_cursor,
10    _marker: marker::PhantomData<&'txn ()>,
11}
12
13impl<'txn> RoCursor<'txn> {
14    pub(crate) fn new(txn: &'txn RoTxn, dbi: ffi::MDB_dbi) -> Result<RoCursor<'txn>> {
15        let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
16        unsafe { mdb_result(ffi::mdb_cursor_open(txn.txn, dbi, &mut cursor))? }
17        Ok(RoCursor { cursor, _marker: marker::PhantomData })
18    }
19
20    pub fn current(&mut self) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
21        let mut key_val = mem::MaybeUninit::uninit();
22        let mut data_val = mem::MaybeUninit::uninit();
23
24        // Move the cursor on the first database key
25        let result = unsafe {
26            mdb_result(ffi::mdb_cursor_get(
27                self.cursor,
28                key_val.as_mut_ptr(),
29                data_val.as_mut_ptr(),
30                ffi::cursor_op::MDB_GET_CURRENT,
31            ))
32        };
33
34        match result {
35            Ok(()) => {
36                let key = unsafe { crate::from_val(key_val.assume_init()) };
37                let data = unsafe { crate::from_val(data_val.assume_init()) };
38                Ok(Some((key, data)))
39            }
40            Err(e) if e.not_found() => Ok(None),
41            Err(e) => Err(e.into()),
42        }
43    }
44
45    pub fn move_on_first(&mut self, op: MoveOperation) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
46        let mut key_val = mem::MaybeUninit::uninit();
47        let mut data_val = mem::MaybeUninit::uninit();
48
49        let flag = match op {
50            MoveOperation::Any => ffi::cursor_op::MDB_FIRST,
51            MoveOperation::Dup => {
52                unsafe {
53                    mdb_result(ffi::mdb_cursor_get(
54                        self.cursor,
55                        ptr::null_mut(),
56                        &mut ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() },
57                        ffi::cursor_op::MDB_FIRST_DUP,
58                    ))?
59                };
60                ffi::cursor_op::MDB_GET_CURRENT
61            }
62            MoveOperation::NoDup => ffi::cursor_op::MDB_FIRST,
63        };
64
65        // Move the cursor on the first database key
66        let result = unsafe {
67            mdb_result(ffi::mdb_cursor_get(
68                self.cursor,
69                key_val.as_mut_ptr(),
70                data_val.as_mut_ptr(),
71                flag,
72            ))
73        };
74
75        match result {
76            Ok(()) => {
77                let key = unsafe { crate::from_val(key_val.assume_init()) };
78                let data = unsafe { crate::from_val(data_val.assume_init()) };
79                Ok(Some((key, data)))
80            }
81            Err(e) if e.not_found() => Ok(None),
82            Err(e) => Err(e.into()),
83        }
84    }
85
86    pub fn move_on_last(&mut self, op: MoveOperation) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
87        let mut key_val = mem::MaybeUninit::uninit();
88        let mut data_val = mem::MaybeUninit::uninit();
89
90        let flag = match op {
91            MoveOperation::Any => ffi::cursor_op::MDB_LAST,
92            MoveOperation::Dup => {
93                unsafe {
94                    mdb_result(ffi::mdb_cursor_get(
95                        self.cursor,
96                        ptr::null_mut(),
97                        &mut ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() },
98                        ffi::cursor_op::MDB_LAST_DUP,
99                    ))?
100                };
101                ffi::cursor_op::MDB_GET_CURRENT
102            }
103            MoveOperation::NoDup => ffi::cursor_op::MDB_LAST,
104        };
105
106        // Move the cursor on the first database key
107        let result = unsafe {
108            mdb_result(ffi::mdb_cursor_get(
109                self.cursor,
110                key_val.as_mut_ptr(),
111                data_val.as_mut_ptr(),
112                flag,
113            ))
114        };
115
116        match result {
117            Ok(()) => {
118                let key = unsafe { crate::from_val(key_val.assume_init()) };
119                let data = unsafe { crate::from_val(data_val.assume_init()) };
120                Ok(Some((key, data)))
121            }
122            Err(e) if e.not_found() => Ok(None),
123            Err(e) => Err(e.into()),
124        }
125    }
126
127    pub fn move_on_key(&mut self, key: &[u8]) -> Result<bool> {
128        let mut key_val = unsafe { crate::into_val(key) };
129
130        // Move the cursor to the specified key
131        let result = unsafe {
132            mdb_result(ffi::mdb_cursor_get(
133                self.cursor,
134                &mut key_val,
135                &mut ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() },
136                ffi::cursor_op::MDB_SET,
137            ))
138        };
139
140        match result {
141            Ok(()) => Ok(true),
142            Err(e) if e.not_found() => Ok(false),
143            Err(e) => Err(e.into()),
144        }
145    }
146
147    pub fn move_on_key_greater_than_or_equal_to(
148        &mut self,
149        key: &[u8],
150    ) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
151        let mut key_val = unsafe { crate::into_val(key) };
152        let mut data_val = mem::MaybeUninit::uninit();
153
154        // Move the cursor to the specified key
155        let result = unsafe {
156            mdb_result(ffi::mdb_cursor_get(
157                self.cursor,
158                &mut key_val,
159                data_val.as_mut_ptr(),
160                ffi::cursor_op::MDB_SET_RANGE,
161            ))
162        };
163
164        match result {
165            Ok(()) => {
166                let key = unsafe { crate::from_val(key_val) };
167                let data = unsafe { crate::from_val(data_val.assume_init()) };
168                Ok(Some((key, data)))
169            }
170            Err(e) if e.not_found() => Ok(None),
171            Err(e) => Err(e.into()),
172        }
173    }
174
175    pub fn move_on_prev(&mut self, op: MoveOperation) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
176        let mut key_val = mem::MaybeUninit::uninit();
177        let mut data_val = mem::MaybeUninit::uninit();
178
179        let flag = match op {
180            MoveOperation::Any => ffi::cursor_op::MDB_PREV,
181            MoveOperation::Dup => ffi::cursor_op::MDB_PREV_DUP,
182            MoveOperation::NoDup => ffi::cursor_op::MDB_PREV_NODUP,
183        };
184
185        // Move the cursor to the previous non-dup key
186        let result = unsafe {
187            mdb_result(ffi::mdb_cursor_get(
188                self.cursor,
189                key_val.as_mut_ptr(),
190                data_val.as_mut_ptr(),
191                flag,
192            ))
193        };
194
195        match result {
196            Ok(()) => {
197                let key = unsafe { crate::from_val(key_val.assume_init()) };
198                let data = unsafe { crate::from_val(data_val.assume_init()) };
199                Ok(Some((key, data)))
200            }
201            Err(e) if e.not_found() => Ok(None),
202            Err(e) => Err(e.into()),
203        }
204    }
205
206    pub fn move_on_next(&mut self, op: MoveOperation) -> Result<Option<(&'txn [u8], &'txn [u8])>> {
207        let mut key_val = mem::MaybeUninit::uninit();
208        let mut data_val = mem::MaybeUninit::uninit();
209
210        let flag = match op {
211            MoveOperation::Any => ffi::cursor_op::MDB_NEXT,
212            MoveOperation::Dup => ffi::cursor_op::MDB_NEXT_DUP,
213            MoveOperation::NoDup => ffi::cursor_op::MDB_NEXT_NODUP,
214        };
215
216        // Move the cursor to the next non-dup key
217        let result = unsafe {
218            mdb_result(ffi::mdb_cursor_get(
219                self.cursor,
220                key_val.as_mut_ptr(),
221                data_val.as_mut_ptr(),
222                flag,
223            ))
224        };
225
226        match result {
227            Ok(()) => {
228                let key = unsafe { crate::from_val(key_val.assume_init()) };
229                let data = unsafe { crate::from_val(data_val.assume_init()) };
230                Ok(Some((key, data)))
231            }
232            Err(e) if e.not_found() => Ok(None),
233            Err(e) => Err(e.into()),
234        }
235    }
236}
237
238impl Drop for RoCursor<'_> {
239    fn drop(&mut self) {
240        unsafe { ffi::mdb_cursor_close(self.cursor) }
241    }
242}
243
244pub struct RwCursor<'txn> {
245    cursor: RoCursor<'txn>,
246}
247
248impl<'txn> RwCursor<'txn> {
249    pub(crate) fn new(txn: &'txn RwTxn, dbi: ffi::MDB_dbi) -> Result<RwCursor<'txn>> {
250        Ok(RwCursor { cursor: RoCursor::new(txn, dbi)? })
251    }
252
253    /// Delete the entry the cursor is currently pointing to.
254    ///
255    /// Returns `true` if the entry was successfully deleted.
256    ///
257    /// # Safety
258    ///
259    /// It is _[undefined behavior]_ to keep a reference of a value from this database
260    /// while modifying it.
261    ///
262    /// > [Values returned from the database are valid only until a subsequent update operation,
263    /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val)
264    ///
265    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
266    pub unsafe fn del_current(&mut self) -> Result<bool> {
267        // Delete the current entry
268        let result = mdb_result(ffi::mdb_cursor_del(self.cursor.cursor, 0));
269
270        match result {
271            Ok(()) => Ok(true),
272            Err(e) if e.not_found() => Ok(false),
273            Err(e) => Err(e.into()),
274        }
275    }
276
277    /// Write a new value to the current entry.
278    ///
279    /// The given key **must** be equal to the one this cursor is pointing otherwise the database
280    /// can be put into an inconsistent state.
281    ///
282    /// Returns `true` if the entry was successfully written.
283    ///
284    /// > This is intended to be used when the new data is the same size as the old.
285    /// > Otherwise it will simply perform a delete of the old record followed by an insert.
286    ///
287    /// # Safety
288    ///
289    /// It is _[undefined behavior]_ to keep a reference of a value from this database while
290    /// modifying it, so you can't use the key/value that comes from the cursor to feed
291    /// this function.
292    ///
293    /// In other words: Transform the key and value that you borrow from this database into an owned
294    /// version of them (e.g. `&str` into `String`).
295    ///
296    /// > [Values returned from the database are valid only until a subsequent update operation,
297    /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val)
298    ///
299    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
300    pub unsafe fn put_current(&mut self, key: &[u8], data: &[u8]) -> Result<bool> {
301        let mut key_val = crate::into_val(key);
302        let mut data_val = crate::into_val(data);
303
304        // Modify the pointed data
305        let result = mdb_result(ffi::mdb_cursor_put(
306            self.cursor.cursor,
307            &mut key_val,
308            &mut data_val,
309            ffi::MDB_CURRENT,
310        ));
311
312        match result {
313            Ok(()) => Ok(true),
314            Err(e) if e.not_found() => Ok(false),
315            Err(e) => Err(e.into()),
316        }
317    }
318
319    /// Write a new value to the current entry.
320    ///
321    /// The given key **must** be equal to the one this cursor is pointing otherwise the database
322    /// can be put into an inconsistent state.
323    ///
324    /// Returns `true` if the entry was successfully written.
325    ///
326    /// > This is intended to be used when the new data is the same size as the old.
327    /// > Otherwise it will simply perform a delete of the old record followed by an insert.
328    ///
329    /// # Safety
330    ///
331    /// Please read the safety notes of the [`Self::put_current`] method.
332    pub unsafe fn put_current_reserved_with_flags<F>(
333        &mut self,
334        flags: PutFlags,
335        key: &[u8],
336        data_size: usize,
337        write_func: F,
338    ) -> Result<bool>
339    where
340        F: FnOnce(&mut ReservedSpace) -> io::Result<()>,
341    {
342        let mut key_val = crate::into_val(key);
343        let mut reserved = ffi::reserve_size_val(data_size);
344        let flags = ffi::MDB_RESERVE | flags.bits();
345
346        let result =
347            mdb_result(ffi::mdb_cursor_put(self.cursor.cursor, &mut key_val, &mut reserved, flags));
348
349        let found = match result {
350            Ok(()) => true,
351            Err(e) if e.not_found() => false,
352            Err(e) => return Err(e.into()),
353        };
354
355        let mut reserved = ReservedSpace::from_val(reserved);
356        write_func(&mut reserved)?;
357
358        if reserved.remaining() == 0 {
359            Ok(found)
360        } else {
361            Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
362        }
363    }
364
365    /// Append the given key/value pair to the end of the database.
366    ///
367    /// If a key is inserted that is less than any previous key a `KeyExist` error
368    /// is returned and the key is not inserted into the database.
369    ///
370    /// # Safety
371    ///
372    /// It is _[undefined behavior]_ to keep a reference of a value from this database while
373    /// modifying it, so you can't use the key/value that comes from the cursor to feed
374    /// this function.
375    ///
376    /// In other words: Transform the key and value that you borrow from this database into an owned
377    /// version of them (e.g. `&str` into `String`).
378    ///
379    /// > [Values returned from the database are valid only until a subsequent update operation,
380    /// > or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val)
381    ///
382    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
383    pub unsafe fn put_current_with_flags(
384        &mut self,
385        flags: PutFlags,
386        key: &[u8],
387        data: &[u8],
388    ) -> Result<()> {
389        let mut key_val = crate::into_val(key);
390        let mut data_val = crate::into_val(data);
391
392        // Modify the pointed data
393        let result = mdb_result(ffi::mdb_cursor_put(
394            self.cursor.cursor,
395            &mut key_val,
396            &mut data_val,
397            flags.bits(),
398        ));
399
400        result.map_err(Into::into)
401    }
402}
403
404impl<'txn> Deref for RwCursor<'txn> {
405    type Target = RoCursor<'txn>;
406
407    fn deref(&self) -> &Self::Target {
408        &self.cursor
409    }
410}
411
412impl DerefMut for RwCursor<'_> {
413    fn deref_mut(&mut self) -> &mut Self::Target {
414        &mut self.cursor
415    }
416}
417
418/// The way the `Iterator::next/prev` method behaves towards DUP data.
419#[derive(Debug, Clone, Copy)]
420pub enum MoveOperation {
421    /// Move on the next/prev entry, wether it's the same key or not.
422    Any,
423    /// Move on the next/prev data of the current key.
424    Dup,
425    /// Move on the next/prev entry which is the next/prev key.
426    /// Skip the multiple values of the current key.
427    NoDup,
428}