Skip to main content

sqlite_containers/
sqlite_map.rs

1// SQLiteMap
2// This file is part of the 'SQLite-based containers for Rust' project (sqlite-containers)
3// SPDX-License-Identifier: Unlicense
4
5use crate::{
6    CharType, Error, Size, SizeExt, Type,
7    common::{SQL_TYPE_TEXT, SQL_TYPES, SqlType, check_constraint_violation},
8    rusqlite::{Connection, OptionalExtension, ToSql, Transaction, params},
9};
10use cfg_if::cfg_if;
11use std::{borrow::Borrow, collections::BTreeMap, marker::PhantomData, sync::LazyLock};
12
13// ---------------------------------------------------------------------------
14// Statements
15// ---------------------------------------------------------------------------
16
17static SQL_CREATE_TBL: LazyLock<BTreeMap<(SqlType, SqlType), String>> = LazyLock::new(|| {
18    let mut map = BTreeMap::<(SqlType, SqlType), String>::new();
19    for key_type in SQL_TYPES {
20        for value_type in SQL_TYPES {
21            cfg_if! {
22                if #[cfg(feature = "with_rowid")] {
23                    map.insert((key_type, value_type), format!("CREATE TABLE data (key {key_type} PRIMARY KEY NOT NULL, value {value_type} NOT NULL);"));
24                } else {
25                    map.insert((key_type, value_type), format!("CREATE TABLE data (key {key_type} PRIMARY KEY NOT NULL, value {value_type} NOT NULL) WITHOUT ROWID;"));
26                }
27            }
28        }
29    }
30    map
31});
32
33static SQL_CREATE_NOC: LazyLock<BTreeMap<SqlType, String>> = LazyLock::new(|| {
34    let mut map = BTreeMap::<SqlType, String>::new();
35    for value_type in SQL_TYPES {
36        cfg_if! {
37            if #[cfg(feature = "with_rowid")] {
38                map.insert(value_type, format!("CREATE TABLE data (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE, value {value_type} NOT NULL);"));
39            } else {
40                map.insert(value_type, format!("CREATE TABLE data (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE, value {value_type} NOT NULL) WITHOUT ROWID;"));
41            }
42        }
43    }
44    map
45});
46
47const SQL_COUNT_KEYS: &str = "SELECT COUNT(*) FROM data;";
48const SQL_INSERT_KEY: &str = "INSERT INTO data (key, value) VALUES (?1, ?2);";
49const SQL_UPSERT_KEY: &str = "INSERT INTO data (key, value) VALUES (?1, ?2) ON CONFLICT(key) DO UPDATE SET value = excluded.value;";
50const SQL_EXISTS_KEY: &str = "SELECT 1 FROM data WHERE key = ? LIMIT 1;";
51const SQL_EXISTS_NOC: &str = "SELECT 1 FROM data WHERE key COLLATE NOCASE = ? LIMIT 1;";
52const SQL_LOOKUP_KEY: &str = "SELECT value FROM data WHERE key = ? LIMIT 1;";
53const SQL_LOOKUP_NOC: &str = "SELECT value FROM data WHERE key COLLATE NOCASE = ? LIMIT 1;";
54const SQL_QUERY_KEYS: &str = "SELECT key FROM data;";
55const SQL_WHERE_KEYS: &str = "SELECT key FROM data WHERE key LIKE ?1 ESCAPE '\\';";
56const SQL_QUERY_PAIR: &str = "SELECT key, value FROM data;";
57const SQL_WHERE_PAIR: &str = "SELECT key, value FROM data WHERE key LIKE ?1 ESCAPE '\\';";
58const SQL_DELETE_KEY: &str = "DELETE FROM data WHERE key = ?;";
59const SQL_DELETE_NOC: &str = "DELETE FROM data WHERE key COLLATE NOCASE = ?;";
60const SQL_DELETE_ALL: &str = "DELETE FROM data;";
61
62// ---------------------------------------------------------------------------
63// SQLiteMap
64// ---------------------------------------------------------------------------
65
66/// A [map](https://en.wikipedia.org/wiki/Associative_array) that stores key-value pairs with keys of type [`K`](crate::Type) and values of type [`V`](crate::Type), backed by an SQLite in-memory database.
67///
68/// It serves as a drop-in replacement for [`HashMap`](std::collections::HashMap).
69///
70/// By default, `SQLiteMap<String>` treats its keys as case-sensitive, but a case-insensitive variant is available. Even when using the case-sensitive map variant, for some operations a dedicated "case-insensitive" version is provided.
71///
72/// &nbsp;
73///
74/// <div class="warning">
75///
76/// For bulk inserts or updates, it is **strongly recommended** to use an explicit [transaction](Self::transaction). Without one, SQLite executes each insert or update in its own transaction, which can significantly degrade performance.
77///
78/// </div>
79pub struct SQLiteMap<K, V>
80where
81    K: Type,
82    V: Type,
83{
84    _types: (PhantomData<K>, PhantomData<V>),
85    connection: Connection,
86}
87
88impl<K, V> SQLiteMap<K, V>
89where
90    K: Type,
91    V: Type,
92{
93    /// Creates a new, empty SQLite-backed hash map with case-sensitive keys.
94    #[inline]
95    pub fn new() -> Result<Self, Error> {
96        Ok(Self { _types: (PhantomData, PhantomData), connection: Self::initialize_connection::<false>()? })
97    }
98
99    /// Creates a new, empty SQLite-backed hash map with case-insensitive keys.
100    ///
101    /// Note that only ASCII characters are case folded. SQLite does **not** attempt to do full UTF case folding!
102    #[inline]
103    pub fn with_nocase() -> Result<Self, Error>
104    where
105        K: CharType,
106    {
107        Ok(Self { _types: (PhantomData, PhantomData), connection: Self::initialize_connection::<true>()? })
108    }
109
110    #[inline]
111    fn initialize_connection<const NO_CASE: bool>() -> Result<Connection, Error> {
112        let connection = Connection::open_in_memory()?;
113        connection.pragma_update(None, "journal_mode", "OFF")?;
114        connection.pragma_update(None, "synchronous", "OFF")?;
115        connection.pragma_update(None, "temp_store", "MEMORY")?;
116        if !NO_CASE {
117            connection.execute(SQL_CREATE_TBL.get(&(K::SQL_TYPE, V::SQL_TYPE)).unwrap(), [])?;
118        } else {
119            assert_eq!(K::SQL_TYPE, SQL_TYPE_TEXT);
120            connection.execute(SQL_CREATE_NOC.get(&V::SQL_TYPE).unwrap(), [])?;
121        }
122        Ok(connection)
123    }
124
125    /// Starts a new SQLite transaction for this map.
126    ///
127    /// Please note that using an explicit SQLite transaction allows for much more efficient bulk inserts &#x1F680;
128    ///
129    /// Returns the new [`SQLiteMapTransaction`] instance.
130    #[inline]
131    pub fn transaction(&mut self) -> Result<SQLiteMapTransaction<'_, K, V>, Error> {
132        SQLiteMapTransaction::from(&mut self.connection)
133    }
134
135    /// Inserts the given key-value pair into the map, if the specified key does *not* exist yet.
136    ///
137    /// If the map already contains the specified key, then its associated value is **not** changed by this operation!
138    ///
139    /// Returns `true`, if the key-value pair was inserted; otherwise returns `false`.
140    ///
141    /// Please use the [`update()`](Self::update) function to update the value associated with a key that may already exist.
142    #[inline]
143    pub fn insert<Q, R>(&mut self, key: &Q, value: &R) -> Result<bool, Error>
144    where
145        K: Borrow<Q>,
146        Q: ?Sized + ToSql,
147        V: Borrow<R>,
148        R: ?Sized + ToSql,
149    {
150        let mut statement = self.connection.prepare_cached(SQL_INSERT_KEY)?;
151        match statement.execute(params![key, value]) {
152            Ok(_) => Ok(true),
153            Err(error) => check_constraint_violation(error),
154        }
155    }
156
157    /// Associates the specified value with the specified key, regardless of whether the key already exists in the map.
158    ///
159    /// If the map does *not* already contain the specified key, then the key is inserted automatically.
160    ///
161    /// **Note:** Due to limitations in SQLite, it is *not* possible to determine whether the key was inserted or updated.
162    #[inline]
163    pub fn update<Q, R>(&mut self, key: &Q, value: &R) -> Result<(), Error>
164    where
165        K: Borrow<Q>,
166        Q: ?Sized + ToSql,
167        V: Borrow<R>,
168        R: ?Sized + ToSql,
169    {
170        let mut statement = self.connection.prepare_cached(SQL_UPSERT_KEY)?;
171        statement.execute(params![key, value])?;
172        Ok(())
173    }
174
175    /// Checks whether the map contains the specified key.
176    ///
177    /// For case-sensitive maps, the check is case-sensitive; for case-insensitive maps, the check is case-insensitive.
178    ///
179    /// Returns `true`, if the map contains the key; otherwise returns `false`.
180    #[inline]
181    pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
182    where
183        K: Borrow<Q>,
184        Q: ?Sized + ToSql,
185    {
186        let mut statement = self.connection.prepare_cached(SQL_EXISTS_KEY)?;
187        Ok(statement.exists([key])?)
188    }
189
190    /// This is the "case-insensitive" version of the [`contains()`](Self::contains_key) function.
191    ///
192    /// The check is *always* performed case-insensitive.
193    ///
194    /// Returns `true`, if the map contains the key; otherwise returns `false`.
195    #[inline]
196    pub fn contains_key_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
197    where
198        K: Borrow<Q> + CharType,
199        Q: ?Sized + ToSql,
200    {
201        let mut statement = self.connection.prepare_cached(SQL_EXISTS_NOC)?;
202        Ok(statement.exists([key])?)
203    }
204
205    /// Tries to retrieve the value for the specified key.
206    ///
207    /// For case-sensitive maps, the key is treated as case-sensitive; for case-insensitive maps, it is treated as case-insensitive.
208    ///
209    /// Returns the `Some(value)`, if the map contains the key; otherwise returns `None`.
210    #[inline]
211    pub fn get<Q>(&self, key: &Q) -> Result<Option<V>, Error>
212    where
213        K: Borrow<Q>,
214        Q: ?Sized + ToSql,
215    {
216        let mut statement = self.connection.prepare_cached(SQL_LOOKUP_KEY)?;
217        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
218    }
219
220    /// This is the "case-insensitive" version of the [`get()`](Self::get) function.
221    ///
222    /// The key is *always* treated as case-insensitive.
223    ///
224    /// Returns the `Some(value)`, if the map contains the key; otherwise returns `None`.
225    #[inline]
226    pub fn get_nocase<Q>(&self, key: &Q) -> Result<Option<V>, Error>
227    where
228        K: Borrow<Q> + CharType,
229        Q: ?Sized + ToSql,
230    {
231        let mut statement = self.connection.prepare_cached(SQL_LOOKUP_NOC)?;
232        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
233    }
234
235    /// Removes the specified key from the map, if present.
236    ///
237    /// For case-sensitive maps, the key is treated as case-sensitive; for case-insensitive maps, it is treated as case-insensitive.
238    ///
239    /// Returns `true`, if the map contained the key; otherwise returns `false`.
240    #[inline]
241    pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
242    where
243        K: Borrow<Q>,
244        Q: ?Sized + ToSql,
245    {
246        let mut statement = self.connection.prepare_cached(SQL_DELETE_KEY)?;
247        Ok(statement.execute([key])? != 0)
248    }
249
250    /// This is the "case-insensitive" version of the [`remove()`](Self::remove) function.
251    ///
252    /// The key is *always* treated as case-insensitive.
253    ///
254    /// Returns `true`, if the map contained the key; otherwise returns `false`.
255    #[inline]
256    pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
257    where
258        K: Borrow<Q> + CharType,
259        Q: ?Sized + ToSql,
260    {
261        let mut statement = self.connection.prepare_cached(SQL_DELETE_NOC)?;
262        Ok(statement.execute([key])? != 0)
263    }
264
265    /// Invokes the given `callback` function for each key that is currently contained in the map.
266    ///
267    /// This function does **not** guarantee a specific iteration order.
268    #[inline]
269    pub fn for_each_key<F>(&self, mut callback: F) -> Result<(), Error>
270    where
271        F: FnMut(K),
272    {
273        let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
274        for key in statement.query_map([], |row| row.get(0))? {
275            callback(key?);
276        }
277        Ok(())
278    }
279
280    /// Invokes the given `callback` function for each key-value pair that is currently contained in the map.
281    ///
282    /// This function does **not** guarantee a specific iteration order.
283    #[inline]
284    pub fn for_each_entry<F>(&self, mut callback: F) -> Result<(), Error>
285    where
286        F: FnMut(K, V),
287    {
288        let mut statement = self.connection.prepare_cached(SQL_QUERY_PAIR)?;
289        let mut result = statement.query([])?;
290        while let Some(key_value) = result.next()? {
291            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
292            callback(key, value);
293        }
294        Ok(())
295    }
296
297    /// Searches the map for the first key that satisfies the given `predicate`.
298    ///
299    /// Returns the first key that satisfies the given predicate, or `None` if none satisfies the predicate or the map is empty.
300    ///
301    /// This function does **not** guarantee a specific iteration order.
302    ///
303    /// Also, the predicate is **not** always tested on *all* keys, because the function returns at the first match.
304    #[inline]
305    pub fn find_key<P>(&self, predicate: P) -> Result<Option<K>, Error>
306    where
307        P: Fn(&K) -> bool,
308    {
309        let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
310        for key in statement.query_map([], |row| row.get(0))? {
311            let key: K = key?;
312            if predicate(key.borrow()) {
313                return Ok(Some(key));
314            }
315        }
316        Ok(None)
317    }
318
319    /// Searches the map for the first key-value pair that satisfies the given `predicate`.
320    ///
321    /// Returns the first key-value pair that satisfies the given predicate, or `None` if none satisfies the predicate or the map is empty.
322    ///
323    /// This function does **not** guarantee a specific iteration order.
324    ///
325    /// Also, the predicate is **not** always tested on *all* key-value pairs, because the function returns at the first match.
326    #[inline]
327    pub fn find_entry<P>(&self, predicate: P) -> Result<Option<(K, V)>, Error>
328    where
329        P: Fn(&K, &V) -> bool,
330    {
331        let mut statement = self.connection.prepare_cached(SQL_QUERY_PAIR)?;
332        let mut result = statement.query([])?;
333        while let Some(key_value) = result.next()? {
334            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
335            if predicate(&key, &value) {
336                return Ok(Some((key, value)));
337            }
338        }
339        Ok(None)
340    }
341
342    /// Invokes the given `callback` function for each key that matches the given pattern.
343    ///
344    /// In the pattern string, the special character `%` matches zero or more characters, the special character `_` matches exactly one character, and the special character `\` serves as an escape character. All other characters are literals.
345    ///
346    /// Please note that SQLite's pattern matching is always *case-insensitive*.
347    ///
348    /// For example, the pattern `A%` matches all keys that begin with the character "A", whereas the pattern `H_nt` matches all keys with a length of exactly four characters where the first character is "H" and the last two characters are "nt".
349    ///
350    /// This function does **not** guarantee a specific iteration order.
351    #[inline]
352    pub fn filter_keys<F, Q>(&self, pattern: &Q, mut callback: F) -> Result<(), Error>
353    where
354        F: FnMut(K),
355        K: Borrow<Q> + CharType,
356        Q: ?Sized + ToSql,
357    {
358        let mut statement = self.connection.prepare_cached(SQL_WHERE_KEYS)?;
359        for key in statement.query_map([pattern], |row| row.get(0))? {
360            callback(key?);
361        }
362        Ok(())
363    }
364
365    /// Invokes the given `callback` function for each key-value pair whose key matches the given pattern.
366    ///
367    /// In the pattern string, the special character `%` matches zero or more characters, the special character `_` matches exactly one character, and the special character `\` serves as an escape character. All other characters are literals.
368    ///
369    /// Please note that SQLite's pattern matching is always *case-insensitive*.
370    ///
371    /// For example, the pattern `A%` matches all keys that begin with the character "A", whereas the pattern `H_nt` matches all keys with a length of exactly four characters where the first character is "H" and the last two characters are "nt".
372    ///
373    /// This function does **not** guarantee a specific iteration order.
374    #[inline]
375    pub fn filter_entries<F, Q>(&self, pattern: &Q, mut callback: F) -> Result<(), Error>
376    where
377        F: FnMut(K, V),
378        K: Borrow<Q> + CharType,
379        Q: ?Sized + ToSql,
380    {
381        let mut statement = self.connection.prepare_cached(SQL_WHERE_PAIR)?;
382        let mut result = statement.query([pattern])?;
383        while let Some(key_value) = result.next()? {
384            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
385            callback(key, value);
386        }
387        Ok(())
388    }
389
390    /// Returns the number of unique keys in the map.
391    #[inline]
392    pub fn len(&self) -> Result<Size, Error> {
393        let mut statement = self.connection.prepare_cached(SQL_COUNT_KEYS)?;
394        let count: i64 = statement.query_one([], |row| row.get(0))?;
395        Ok(Size::from_i64(count))
396    }
397
398    /// Returns `true` if the map contains **no** keys; otherwise returns `false`.
399    #[inline]
400    pub fn is_empty(&self) -> Result<bool, Error> {
401        Ok(self.len()? == 0)
402    }
403
404    /// Removes *all* keys from the map.
405    #[inline]
406    pub fn clear(&mut self) -> Result<(), Error> {
407        let mut statement = self.connection.prepare_cached(SQL_DELETE_ALL)?;
408        statement.execute([])?;
409        Ok(())
410    }
411}
412
413// ---------------------------------------------------------------------------
414// SQLiteMap Transaction
415// ---------------------------------------------------------------------------
416
417/// Represents an active SQLite transaction for a [`SQLiteMap`].
418///
419/// Most functions provided by this struct mirror the corresponding functions of the `SQLiteMap` struct.
420///
421/// The transaction is committed when the `SQLiteMapTransaction` is dropped.
422pub struct SQLiteMapTransaction<'a, K, V>
423where
424    K: Type,
425    V: Type,
426{
427    _types: (PhantomData<K>, PhantomData<V>),
428    transaction: Transaction<'a>,
429}
430
431impl<'a, K, V> SQLiteMapTransaction<'a, K, V>
432where
433    K: Type,
434    V: Type,
435{
436    #[inline]
437    fn from(connection: &'a mut Connection) -> Result<Self, Error> {
438        let mut transaction = connection.transaction()?;
439        transaction.set_drop_behavior(rusqlite::DropBehavior::Commit);
440        Ok(Self { _types: (PhantomData, PhantomData), transaction })
441    }
442
443    /// Drops the `SQLiteMapTransaction`, thereby committing the SQLite transaction.
444    #[inline]
445    pub fn commit(self) {}
446
447    /// This function is equivalent to [`SQLiteMap::insert()`].
448    #[inline]
449    pub fn insert<Q, R>(&mut self, key: &Q, value: &R) -> Result<bool, Error>
450    where
451        K: Borrow<Q>,
452        Q: ?Sized + ToSql,
453        V: Borrow<R>,
454        R: ?Sized + ToSql,
455    {
456        let mut statement = self.transaction.prepare_cached(SQL_INSERT_KEY)?;
457        match statement.execute(params![key, value]) {
458            Ok(_) => Ok(true),
459            Err(error) => check_constraint_violation(error),
460        }
461    }
462
463    /// This function is equivalent to [`SQLiteMap::update()`].
464    #[inline]
465    pub fn update<Q, R>(&mut self, key: &Q, value: &R) -> Result<(), Error>
466    where
467        K: Borrow<Q>,
468        Q: ?Sized + ToSql,
469        V: Borrow<R>,
470        R: ?Sized + ToSql,
471    {
472        let mut statement = self.transaction.prepare_cached(SQL_UPSERT_KEY)?;
473        statement.execute(params![key, value])?;
474        Ok(())
475    }
476
477    /// This function is equivalent to [`SQLiteMap::contains_key()`].
478    #[inline]
479    pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
480    where
481        K: Borrow<Q>,
482        Q: ?Sized + ToSql,
483    {
484        let mut statement = self.transaction.prepare_cached(SQL_EXISTS_KEY)?;
485        Ok(statement.exists([key])?)
486    }
487
488    /// This function is equivalent to [`SQLiteMap::contains_key_nocase()`].
489    #[inline]
490    pub fn contains_key_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
491    where
492        K: Borrow<Q> + CharType,
493        Q: ?Sized + ToSql,
494    {
495        let mut statement = self.transaction.prepare_cached(SQL_EXISTS_NOC)?;
496        Ok(statement.exists([key])?)
497    }
498
499    /// This function is equivalent to [`SQLiteMap::get()`].
500    #[inline]
501    pub fn get<Q>(&self, key: &Q) -> Result<Option<V>, Error>
502    where
503        K: Borrow<Q>,
504        Q: ?Sized + ToSql,
505    {
506        let mut statement = self.transaction.prepare_cached(SQL_LOOKUP_KEY)?;
507        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
508    }
509
510    /// This function is equivalent to [`SQLiteMap::get_nocase()`].
511    #[inline]
512    pub fn get_nocase<Q>(&self, key: &Q) -> Result<Option<V>, Error>
513    where
514        K: Borrow<Q> + CharType,
515        Q: ?Sized + ToSql,
516    {
517        let mut statement = self.transaction.prepare_cached(SQL_LOOKUP_NOC)?;
518        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
519    }
520
521    /// This function is equivalent to [`SQLiteMap::remove()`].
522    #[inline]
523    pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
524    where
525        K: Borrow<Q>,
526        Q: ?Sized + ToSql,
527    {
528        let mut statement = self.transaction.prepare_cached(SQL_DELETE_KEY)?;
529        Ok(statement.execute([key])? != 0)
530    }
531
532    /// This function is equivalent to [`SQLiteMap::remove_nocase()`].
533    #[inline]
534    pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
535    where
536        K: Borrow<Q> + CharType,
537        Q: ?Sized + ToSql,
538    {
539        let mut statement = self.transaction.prepare_cached(SQL_DELETE_NOC)?;
540        Ok(statement.execute([key])? != 0)
541    }
542
543    /// This function is equivalent to [`SQLiteMap::for_each_key()`].
544    #[inline]
545    pub fn for_each_key<F>(&self, mut callback: F) -> Result<(), Error>
546    where
547        F: FnMut(K),
548    {
549        let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
550        for key in statement.query_map([], |row| row.get(0))? {
551            callback(key?);
552        }
553        Ok(())
554    }
555
556    /// This function is equivalent to [`SQLiteMap::for_each_entry()`].
557    #[inline]
558    pub fn for_each_entry<F>(&self, mut callback: F) -> Result<(), Error>
559    where
560        F: FnMut(K, V),
561    {
562        let mut statement = self.transaction.prepare_cached(SQL_QUERY_PAIR)?;
563        let mut result = statement.query([])?;
564        while let Some(key_value) = result.next()? {
565            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
566            callback(key, value);
567        }
568        Ok(())
569    }
570
571    /// This function is equivalent to [`SQLiteMap::find_key()`].
572    #[inline]
573    pub fn find_key<P>(&self, predicate: P) -> Result<Option<K>, Error>
574    where
575        P: Fn(&K) -> bool,
576    {
577        let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
578        for key in statement.query_map([], |row| row.get(0))? {
579            let key: K = key?;
580            if predicate(key.borrow()) {
581                return Ok(Some(key));
582            }
583        }
584        Ok(None)
585    }
586
587    /// This function is equivalent to [`SQLiteMap::find_entry()`].
588    #[inline]
589    pub fn find_entry<P>(&self, predicate: P) -> Result<Option<(K, V)>, Error>
590    where
591        P: Fn(&K, &V) -> bool,
592    {
593        let mut statement = self.transaction.prepare_cached(SQL_QUERY_PAIR)?;
594        let mut result = statement.query([])?;
595        while let Some(key_value) = result.next()? {
596            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
597            if predicate(&key, &value) {
598                return Ok(Some((key, value)));
599            }
600        }
601        Ok(None)
602    }
603
604    /// This function is equivalent to [`SQLiteMap::filter_keys()`].
605    #[inline]
606    pub fn filter_keys<F, Q>(&self, pattern: &Q, mut callback: F) -> Result<(), Error>
607    where
608        F: FnMut(K),
609        K: Borrow<Q> + CharType,
610        Q: ?Sized + ToSql,
611    {
612        let mut statement = self.transaction.prepare_cached(SQL_WHERE_KEYS)?;
613        for key in statement.query_map([pattern], |row| row.get(0))? {
614            callback(key?);
615        }
616        Ok(())
617    }
618
619    /// This function is equivalent to [`SQLiteMap::filter_entries()`].
620    #[inline]
621    pub fn filter_entries<F, Q>(&self, pattern: &Q, mut callback: F) -> Result<(), Error>
622    where
623        F: FnMut(K, V),
624        K: Borrow<Q> + CharType,
625        Q: ?Sized + ToSql,
626    {
627        let mut statement = self.transaction.prepare_cached(SQL_WHERE_PAIR)?;
628        let mut result = statement.query([pattern])?;
629        while let Some(key_value) = result.next()? {
630            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
631            callback(key, value);
632        }
633        Ok(())
634    }
635
636    /// This function is equivalent to [`SQLiteMap::len()`].
637    #[inline]
638    pub fn len(&self) -> Result<Size, Error> {
639        let mut statement = self.transaction.prepare_cached(SQL_COUNT_KEYS)?;
640        let count: i64 = statement.query_one([], |row| row.get(0))?;
641        Ok(Size::from_i64(count))
642    }
643
644    /// This function is equivalent to [`SQLiteMap::is_empty()`].
645    #[inline]
646    pub fn is_empty(&self) -> Result<bool, Error> {
647        Ok(self.len()? == 0)
648    }
649
650    /// This function is equivalent to [`SQLiteMap::clear()`].
651    #[inline]
652    pub fn clear(&mut self) -> Result<(), Error> {
653        let mut statement = self.transaction.prepare_cached(SQL_DELETE_ALL)?;
654        statement.execute([])?;
655        Ok(())
656    }
657}