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, SQLiteSet, Size, SizeExt, TryClone, Type,
7    common::{OPEN_FLAGS, OPEN_FLAGS_NOCREATE, OPEN_FLAGS_RDONLY, SQL_TYPE_TEXT, SQL_TYPES, SqlType, check_constraint_violation, verify_schema},
8    rusqlite::{Connection, OptionalExtension, ToSql, Transaction, params},
9};
10use cfg_if::cfg_if;
11use const_format::concatcp;
12use std::{borrow::Borrow, collections::BTreeMap, marker::PhantomData, path::Path, sync::LazyLock};
13
14// ---------------------------------------------------------------------------
15// Action Type
16// ---------------------------------------------------------------------------
17
18/// Represents the outcome of an [`update()`](SQLiteMap::update) operation.
19#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub enum Action {
21    /// It is *unknown* whether a new key-value pair was inserted or the value associated with an existing key was updated.
22    ///
23    /// This is the expected return value, if the [`hooks`](crate#optional-features) feature is **not** enabled.
24    Unknown,
25    /// Indicates that a new key-value pair was inserted.
26    Insert,
27    /// Indicates that the value associated with an existing key was updated.
28    ///
29    /// Please note that [`Action::Update`](Self::Update) does **not** guarantee that the value associated with the key has actually changed – it is possible to “update” the associated value of a key to a new value that is equal to the previously associated value.
30    Update,
31}
32
33cfg_if! {
34    if #[cfg(feature = "hooks")] {
35        use crate::rusqlite::hooks::Action as RusqliteAction;
36        use std::sync::{Arc, atomic::{Ordering::Relaxed, AtomicI32}};
37        struct LastActionHolder(Arc<AtomicI32>);
38
39        impl Action {
40            #[inline]
41            fn from(value: &LastActionHolder) -> Self {
42                const ACTION_SQLITE_INSERT: i32 = RusqliteAction::SQLITE_INSERT as i32;
43                const ACTION_SQLITE_UPDATE: i32 = RusqliteAction::SQLITE_UPDATE as i32;
44                match value.0.load(Relaxed) {
45                    ACTION_SQLITE_INSERT => Action::Insert,
46                    ACTION_SQLITE_UPDATE => Action::Update,
47                    _ => Action::Unknown,
48                }
49            }
50        }
51
52        impl Default for LastActionHolder {
53            #[inline]
54            fn default() -> Self {
55                Self(Arc::new(AtomicI32::new(RusqliteAction::UNKNOWN as i32)))
56            }
57        }
58    } else {
59        struct LastActionHolder;
60
61        impl Action {
62            #[inline(always)]
63            const fn from(_: &LastActionHolder) -> Self {
64                Action::Unknown
65            }
66        }
67
68        impl LastActionHolder {
69            #[inline(always)]
70            pub const fn default() -> Self {
71                Self
72            }
73        }
74    }
75}
76
77// ---------------------------------------------------------------------------
78// Statements
79// ---------------------------------------------------------------------------
80
81const TABLE_NAME: &str = "rusqlite_map_v2";
82
83static SQL_CREATE_TBL: LazyLock<BTreeMap<(SqlType, SqlType), String>> = LazyLock::new(|| {
84    let mut map = BTreeMap::<(SqlType, SqlType), String>::new();
85    for key_type in SQL_TYPES {
86        for value_type in SQL_TYPES {
87            cfg_if! {
88                if #[cfg(feature = "with_rowid")] {
89                    map.insert((key_type, value_type), format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key {key_type} PRIMARY KEY NOT NULL, value {value_type} NOT NULL);"));
90                } else {
91                    map.insert((key_type, value_type), format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key {key_type} PRIMARY KEY NOT NULL, value {value_type} NOT NULL) WITHOUT ROWID;"));
92                }
93            }
94        }
95    }
96    map
97});
98
99static SQL_CREATE_NOC: LazyLock<BTreeMap<SqlType, String>> = LazyLock::new(|| {
100    let mut map = BTreeMap::<SqlType, String>::new();
101    for value_type in SQL_TYPES {
102        cfg_if! {
103            if #[cfg(feature = "with_rowid")] {
104                map.insert(value_type, format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE, value {value_type} NOT NULL);"));
105            } else {
106                map.insert(value_type, format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE, value {value_type} NOT NULL) WITHOUT ROWID;"));
107            }
108        }
109    }
110    map
111});
112
113const SQL_GET_SCHEMA: &str = concatcp!("SELECT sql FROM sqlite_schema WHERE type = 'table' AND name = '", TABLE_NAME, "';");
114const SQL_COUNT_KEYS: &str = concatcp!("SELECT COUNT(*) FROM ", TABLE_NAME, ";");
115const SQL_EXIST_KEYS: &str = concatcp!("SELECT EXISTS (SELECT 1 FROM ", TABLE_NAME, ");");
116const SQL_INSERT_KEY: &str = concatcp!("INSERT INTO ", TABLE_NAME, " (key, value) VALUES (?1, ?2);");
117const SQL_UPSERT_KEY: &str = concatcp!("INSERT INTO ", TABLE_NAME, " (key, value) VALUES (?1, ?2) ON CONFLICT(key) DO UPDATE SET value = excluded.value;");
118const SQL_EXISTS_KEY: &str = concatcp!("SELECT 1 FROM ", TABLE_NAME, " WHERE key = ? LIMIT 1;");
119const SQL_EXISTS_NOC: &str = concatcp!("SELECT 1 FROM ", TABLE_NAME, " WHERE key COLLATE NOCASE = ? LIMIT 1;");
120const SQL_LOOKUP_KEY: &str = concatcp!("SELECT value FROM ", TABLE_NAME, " WHERE key = ? LIMIT 1;");
121const SQL_LOOKUP_NOC: &str = concatcp!("SELECT value FROM ", TABLE_NAME, " WHERE key COLLATE NOCASE = ? LIMIT 1;");
122const SQL_QUERY_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, ";");
123const SQL_QUERY_PAIR: &str = concatcp!("SELECT key, value FROM ", TABLE_NAME, ";");
124const SQL_GLOBA_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, " WHERE key GLOB ?1;");
125const SQL_GLOBN_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, " WHERE key NOT GLOB ?1;");
126const SQL_GLOBA_PAIR: &str = concatcp!("SELECT key, value FROM ", TABLE_NAME, " WHERE key GLOB ?1;");
127const SQL_GLOBN_PAIR: &str = concatcp!("SELECT key, value FROM ", TABLE_NAME, " WHERE key NOT GLOB ?1;");
128const SQL_DELETE_KEY: &str = concatcp!("DELETE FROM ", TABLE_NAME, " WHERE key = ?;");
129const SQL_DELETE_NOC: &str = concatcp!("DELETE FROM ", TABLE_NAME, " WHERE key COLLATE NOCASE = ?;");
130const SQL_DELETE_ALL: &str = concatcp!("DELETE FROM ", TABLE_NAME, ";");
131
132// ---------------------------------------------------------------------------
133// SQLiteMap
134// ---------------------------------------------------------------------------
135
136/// 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.
137///
138/// It serves as a drop-in replacement for [`HashMap`](std::collections::HashMap).
139///
140/// 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.
141///
142/// &nbsp;
143///
144/// <div class="warning">
145///
146/// 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.
147///
148/// </div>
149pub struct SQLiteMap<K, V>
150where
151    K: Type,
152    V: Type,
153{
154    _types: (PhantomData<K>, PhantomData<V>),
155    connection: Connection,
156    last_action: LastActionHolder,
157}
158
159impl<K, V> SQLiteMap<K, V>
160where
161    K: Type,
162    V: Type,
163{
164    /// Creates a new, empty hash map with case-sensitive keys, backed by an *in-memory* SQLite database.
165    #[allow(clippy::let_unit_value)]
166    #[inline]
167    pub fn new() -> Result<Self, Error> {
168        Self::from_connection(Self::create_connection::<false, &Path>(None, false, false)?)
169    }
170
171    /// Creates a new, empty hash map with case-insensitive keys, backed by an *in-memory* SQLite database.
172    ///
173    /// Only ASCII characters are case folded. SQLite does **not** attempt to do full UTF case folding!
174    #[allow(clippy::let_unit_value)]
175    #[inline]
176    pub fn with_nocase() -> Result<Self, Error>
177    where
178        K: CharType,
179    {
180        Self::from_connection(Self::create_connection::<true, &Path>(None, false, false)?)
181    }
182
183    /// Creates a new or re-opens an existing hash map with case-sensitive keys, backed by a *file-based* SQLite database.
184    ///
185    /// If `readonly` is `true`, the database file is opened in “read-only” mode.
186    ///
187    /// If `can_create` is `true` (and if `readonly` is `false`), a new empty database file is created, if the file does *not* exist yet.
188    ///
189    /// Returns [`Error::InvalidSchema`], if the database file already exists but its schema does **not** match the schema expected by this type of map. This means that you **must** re-open a database file with the *same* type of map that was used to create it.
190    #[inline]
191    pub fn from_file<P>(path: P, readonly: bool, can_create: bool) -> Result<Self, Error>
192    where
193        P: AsRef<Path>,
194    {
195        Self::from_connection(Self::create_connection::<false, _>(Some(path), readonly, can_create)?)
196    }
197
198    /// Creates a new or re-opens an existing hash map with case-insensitive keys, backed by a *file-based* SQLite database.
199    ///
200    /// If `readonly` is `true`, the database file is opened in “read-only” mode.
201    ///
202    /// If `can_create` is `true` (and if `readonly` is `false`), a new empty database file is created if the file does *not* exist yet.
203    ///
204    /// Returns [`Error::InvalidSchema`], if the database file already exists but its schema does **not** match the schema expected by this type of map. This means that you **must** re-open a database file with the *same* type of map that was used to create it.
205    ///
206    /// Only ASCII characters are case folded. SQLite does **not** attempt to do full UTF case folding!
207    #[inline]
208    pub fn from_file_nocase<P>(path: P, readonly: bool, can_create: bool) -> Result<Self, Error>
209    where
210        K: CharType,
211        P: AsRef<Path>,
212    {
213        Self::from_connection(Self::create_connection::<true, _>(Some(path), readonly, can_create)?)
214    }
215
216    #[inline]
217    fn create_connection<const NO_CASE: bool, P>(path_option: Option<P>, readonly: bool, can_create: bool) -> Result<Connection, Error>
218    where
219        P: AsRef<Path>,
220    {
221        let connection = if let Some(path) = path_option.as_ref() {
222            Connection::open_with_flags(path, if !readonly { if can_create { *OPEN_FLAGS } else { *OPEN_FLAGS_NOCREATE } } else { *OPEN_FLAGS_RDONLY })?
223        } else {
224            Connection::open_in_memory()?
225        };
226
227        let connection = Self::initialize_connection(connection)?;
228
229        #[cfg(feature = "checked")]
230        if path_option.is_some() {
231            crate::common::integrity_check(&connection)?;
232        }
233
234        let sql_create_table = if !NO_CASE {
235            SQL_CREATE_TBL.get(&(K::SQL_TYPE, V::SQL_TYPE)).unwrap()
236        } else {
237            debug_assert_eq!(K::SQL_TYPE, SQL_TYPE_TEXT);
238            SQL_CREATE_NOC.get(&V::SQL_TYPE).unwrap()
239        };
240
241        connection.execute(sql_create_table, [])?;
242
243        if path_option.is_some() {
244            let sql_schema_table: String = connection.query_one(SQL_GET_SCHEMA, [], |row| row.get(0))?;
245            if !verify_schema(sql_create_table, &sql_schema_table) {
246                return Err(Error::InvalidSchema);
247            }
248        }
249
250        Ok(connection)
251    }
252
253    #[inline(always)]
254    fn initialize_connection(connection: Connection) -> Result<Connection, Error> {
255        connection.pragma_update(None, "synchronous", "OFF")?;
256        connection.pragma_update(None, "journal_mode", "OFF")?;
257        Ok(connection)
258    }
259
260    #[inline(always)]
261    fn from_connection(connection: Connection) -> Result<Self, Error> {
262        let last_action = LastActionHolder::default();
263        Self::install_hook(&connection, &last_action);
264        Ok(Self { _types: (PhantomData, PhantomData), connection, last_action })
265    }
266
267    cfg_if! {
268        if #[cfg(feature = "hooks")] {
269            #[inline(always)]
270            fn install_hook(connection: &Connection, last_action: &LastActionHolder) {
271                let last_action = Arc::clone(&last_action.0);
272                connection
273                    .update_hook(Some(move |action, _db: &str, _table: &str, _rowid: i64| {
274                        last_action.store(action as i32, Relaxed);
275                    }))
276                    .unwrap();
277            }
278        } else {
279            #[inline(always)]
280            fn install_hook(_: &Connection, _: &LastActionHolder) {}
281        }
282    }
283
284    /// Starts a new SQLite transaction for this map.
285    ///
286    /// Please note that using an explicit SQLite transaction allows for much more efficient bulk inserts &#x1F680;
287    ///
288    /// Returns the new [`SQLiteMapTransaction`] instance.
289    #[inline]
290    pub fn transaction(&mut self) -> Result<SQLiteMapTransaction<'_, K, V>, Error> {
291        SQLiteMapTransaction::from(&mut self.connection, &self.last_action)
292    }
293
294    /// Inserts the given key-value pair into the map, if the specified key does *not* exist yet.
295    ///
296    /// If the map already contains the specified key, then its associated value is **not** changed by this operation!
297    ///
298    /// Returns `true`, if the key-value pair was inserted; otherwise returns `false`.
299    ///
300    /// Please use the [`update()`](Self::update) function to update the value associated with a key that may already exist.
301    #[inline]
302    pub fn insert<Q, R>(&mut self, key: &Q, value: &R) -> Result<bool, Error>
303    where
304        K: Borrow<Q>,
305        Q: ?Sized + ToSql,
306        V: Borrow<R>,
307        R: ?Sized + ToSql,
308    {
309        let mut statement = self.connection.prepare_cached(SQL_INSERT_KEY)?;
310        match statement.execute(params![key, value]) {
311            Ok(_) => Ok(true),
312            Err(error) => check_constraint_violation(error),
313        }
314    }
315
316    /// Associates the specified value with the specified key, regardless of whether the key already exists in the map.
317    ///
318    /// If the map does *not* already contain the specified key, then the key is inserted automatically.
319    ///
320    /// Provided that the [`hooks`](crate#optional-features) feature is enabled, the function returns either [`Action::Insert`] or [`Action::Update`], depending on whether a new key-value pair was inserted or the associated value of an existing key was updated
321    ///
322    /// If `hooks` is **not** enabled, the function *always* returns [`Action::Unknown`], but, apart from that, it works identically &#128522;
323    #[inline]
324    pub fn update<Q, R>(&mut self, key: &Q, value: &R) -> Result<Action, Error>
325    where
326        K: Borrow<Q>,
327        Q: ?Sized + ToSql,
328        V: Borrow<R>,
329        R: ?Sized + ToSql,
330    {
331        let mut statement = self.connection.prepare_cached(SQL_UPSERT_KEY)?;
332        statement.execute(params![key, value])?;
333        Ok(Action::from(&self.last_action))
334    }
335
336    /// Inserts all key-value pairs from another map into this map.
337    ///
338    /// If `update` is `true`, this function updates the values associated with existing keys in this map using the corresponding values from the other map. Otherwise, the values associated with existing keys are **not** changed.
339    ///
340    /// Returns `Some((inserted, updated))`, i.e., the number of new keys inserted and existing keys updated, respectively.
341    ///
342    /// If [`hooks`](crate#optional-features) is **not** enabled and if `update` is `true`, the function *always* returns `None`, though &#128517;
343    #[inline]
344    pub fn union(&mut self, other: &Self, update: bool) -> Result<Option<(Size, Size)>, Error> {
345        let trx = self.transaction()?;
346        let mut statement_insert = trx.transaction.prepare_cached(if update { SQL_UPSERT_KEY } else { SQL_INSERT_KEY })?;
347        let mut statement_query = other.connection.prepare_cached(SQL_QUERY_PAIR)?;
348        let mut result = statement_query.query([])?;
349        let mut counter = (Size::default(), Size::default());
350        while let Some(key_value) = result.next()? {
351            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
352            if !update {
353                match statement_insert.execute(params![key, value]) {
354                    Ok(_) => counter.0 = counter.0.saturating_add(1),
355                    Err(error) => {
356                        check_constraint_violation(error)?;
357                    }
358                }
359            } else {
360                statement_insert.execute(params![key, value])?;
361                #[cfg(feature = "hooks")]
362                update_counter(&mut counter, Action::from(trx.last_action));
363            }
364        }
365        cfg_if! {
366            if #[cfg(feature = "hooks")] {
367                Ok(Some(counter))
368            } else {
369                Ok(if !update { Some(counter) } else { None })
370            }
371        }
372    }
373
374    /// Checks whether the map contains the specified key.
375    ///
376    /// For case-sensitive maps, the check is case-sensitive; for case-insensitive maps, the check is case-insensitive.
377    ///
378    /// Returns `true`, if the map contains the key; otherwise returns `false`.
379    #[inline]
380    pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
381    where
382        K: Borrow<Q>,
383        Q: ?Sized + ToSql,
384    {
385        let mut statement = self.connection.prepare_cached(SQL_EXISTS_KEY)?;
386        Ok(statement.exists([key])?)
387    }
388
389    /// This is the "case-insensitive" version of the [`contains()`](Self::contains_key) function.
390    ///
391    /// The check is *always* performed case-insensitive.
392    ///
393    /// Returns `true`, if the map contains the key; otherwise returns `false`.
394    #[inline]
395    pub fn contains_key_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
396    where
397        K: Borrow<Q> + CharType,
398        Q: ?Sized + ToSql,
399    {
400        let mut statement = self.connection.prepare_cached(SQL_EXISTS_NOC)?;
401        Ok(statement.exists([key])?)
402    }
403
404    /// Tries to retrieve the value for the specified key.
405    ///
406    /// For case-sensitive maps, the key is treated as case-sensitive; for case-insensitive maps, it is treated as case-insensitive.
407    ///
408    /// Returns the `Some(value)`, if the map contains the key; otherwise returns `None`.
409    #[inline]
410    pub fn get<Q>(&self, key: &Q) -> Result<Option<V>, Error>
411    where
412        K: Borrow<Q>,
413        Q: ?Sized + ToSql,
414    {
415        let mut statement = self.connection.prepare_cached(SQL_LOOKUP_KEY)?;
416        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
417    }
418
419    /// This is the "case-insensitive" version of the [`get()`](Self::get) function.
420    ///
421    /// The key is *always* treated as case-insensitive.
422    ///
423    /// Returns the `Some(value)`, if the map contains the key; otherwise returns `None`.
424    #[inline]
425    pub fn get_nocase<Q>(&self, key: &Q) -> Result<Option<V>, Error>
426    where
427        K: Borrow<Q> + CharType,
428        Q: ?Sized + ToSql,
429    {
430        let mut statement = self.connection.prepare_cached(SQL_LOOKUP_NOC)?;
431        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
432    }
433
434    /// Removes the specified key from the map, if present.
435    ///
436    /// For case-sensitive maps, the key is treated as case-sensitive; for case-insensitive maps, it is treated as case-insensitive.
437    ///
438    /// Returns `true`, if the map contained the key; otherwise returns `false`.
439    #[inline]
440    pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
441    where
442        K: Borrow<Q>,
443        Q: ?Sized + ToSql,
444    {
445        let mut statement = self.connection.prepare_cached(SQL_DELETE_KEY)?;
446        Ok(statement.execute([key])? != 0)
447    }
448
449    /// This is the "case-insensitive" version of the [`remove()`](Self::remove) function.
450    ///
451    /// The key is *always* treated as case-insensitive.
452    ///
453    /// Returns `true`, if the map contained the key; otherwise returns `false`.
454    #[inline]
455    pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
456    where
457        K: Borrow<Q> + CharType,
458        Q: ?Sized + ToSql,
459    {
460        let mut statement = self.connection.prepare_cached(SQL_DELETE_NOC)?;
461        Ok(statement.execute([key])? != 0)
462    }
463
464    /// Invokes the given `callback` function for each key that is currently contained in the map.
465    ///
466    /// This function does **not** guarantee a specific iteration order.
467    #[inline]
468    pub fn for_each_key<F>(&self, mut callback: F) -> Result<(), Error>
469    where
470        F: FnMut(K),
471    {
472        let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
473        for key in statement.query_map([], |row| row.get(0))? {
474            callback(key?);
475        }
476        Ok(())
477    }
478
479    /// Invokes the given `callback` function for each key-value pair that is currently contained in the map.
480    ///
481    /// This function does **not** guarantee a specific iteration order.
482    #[inline]
483    pub fn for_each_entry<F>(&self, mut callback: F) -> Result<(), Error>
484    where
485        F: FnMut(K, V),
486    {
487        let mut statement = self.connection.prepare_cached(SQL_QUERY_PAIR)?;
488        let mut result = statement.query([])?;
489        while let Some(key_value) = result.next()? {
490            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
491            callback(key, value);
492        }
493        Ok(())
494    }
495
496    /// Searches the map for the first key that satisfies the given `predicate`.
497    ///
498    /// Returns the first key that satisfies the given predicate, or `None` if none satisfies the predicate or the map is empty.
499    ///
500    /// This function does **not** guarantee a specific iteration order.
501    ///
502    /// Also, the predicate is **not** always tested on *all* keys, because the function returns at the first match.
503    #[inline]
504    pub fn find_key<P>(&self, predicate: P) -> Result<Option<K>, Error>
505    where
506        P: Fn(&K) -> bool,
507    {
508        let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
509        for key in statement.query_map([], |row| row.get(0))? {
510            let key: K = key?;
511            if predicate(key.borrow()) {
512                return Ok(Some(key));
513            }
514        }
515        Ok(None)
516    }
517
518    /// Searches the map for the first key-value pair that satisfies the given `predicate`.
519    ///
520    /// Returns the first key-value pair that satisfies the given predicate, or `None` if none satisfies the predicate or the map is empty.
521    ///
522    /// This function does **not** guarantee a specific iteration order.
523    ///
524    /// Also, the predicate is **not** always tested on *all* key-value pairs, because the function returns at the first match.
525    #[inline]
526    pub fn find_entry<P>(&self, predicate: P) -> Result<Option<(K, V)>, Error>
527    where
528        P: Fn(&K, &V) -> bool,
529    {
530        let mut statement = self.connection.prepare_cached(SQL_QUERY_PAIR)?;
531        let mut result = statement.query([])?;
532        while let Some(key_value) = result.next()? {
533            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
534            if predicate(&key, &value) {
535                return Ok(Some((key, value)));
536            }
537        }
538        Ok(None)
539    }
540
541    /// Invokes the given `callback` function for each key that matches the given pattern.
542    ///
543    /// The pattern string is interpreted according to the [UNIX *globbing*](https://man7.org/linux/man-pages/man7/glob.7.html) rules. Specifically, a `?` character (not between brackets) matches any single character, and a `*` character (not between brackets) matches any string, including the empty string. Additionally, an expression `[…]` where the first character after the leading `[` is *not* an `!` matches a single character that can be any of the characters enclosed by the brackets; and an expression `[!…]` matches a single character that is *not* matched by the expression.
544    ///
545    /// This function is case-sensitive and does **not** guarantee a specific iteration order.
546    ///
547    /// If `invert` is `true`, returns all keys that do **not** match the pattern; otherwise the matching keys are returned.
548    #[inline]
549    pub fn glob_keys<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
550    where
551        F: FnMut(K),
552        K: Borrow<Q> + CharType,
553        Q: ?Sized + ToSql,
554    {
555        let mut statement = self.connection.prepare_cached(if invert { SQL_GLOBN_KEYS } else { SQL_GLOBA_KEYS })?;
556        for key in statement.query_map([pattern], |row| row.get(0))? {
557            callback(key?);
558        }
559        Ok(())
560    }
561
562    /// Invokes the given `callback` function for each key-value pair whose key matches the given pattern.
563    ///
564    /// The pattern string is interpreted according to the [UNIX *globbing*](https://man7.org/linux/man-pages/man7/glob.7.html) rules. Specifically, a `?` character (not between brackets) matches any single character, and a `*` character (not between brackets) matches any string, including the empty string. Additionally, an expression `[…]` where the first character after the leading `[` is *not* an `!` matches a single character that can be any of the characters enclosed by the brackets; and an expression `[!…]` matches a single character that is *not* matched by the expression.
565    ///
566    /// This function is case-sensitive and does **not** guarantee a specific iteration order.
567    ///
568    /// If `invert` is `true`, returns all key-value pairs that do **not** match the pattern; otherwise the matching pairs are returned.
569    #[inline]
570    pub fn glob_entries<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
571    where
572        F: FnMut(K, V),
573        K: Borrow<Q> + CharType,
574        Q: ?Sized + ToSql,
575    {
576        let mut statement = self.connection.prepare_cached(if invert { SQL_GLOBN_PAIR } else { SQL_GLOBA_PAIR })?;
577        let mut result = statement.query([pattern])?;
578        while let Some(key_value) = result.next()? {
579            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
580            callback(key, value);
581        }
582        Ok(())
583    }
584
585    /// Returns the number of unique keys in the map.
586    #[inline]
587    pub fn len(&self) -> Result<Size, Error> {
588        let mut statement = self.connection.prepare_cached(SQL_COUNT_KEYS)?;
589        let count: i64 = statement.query_one([], |row| row.get(0))?;
590        Ok(Size::from_i64(count))
591    }
592
593    /// Returns `true` if the map contains **no** keys; otherwise returns `false`.
594    #[inline]
595    pub fn is_empty(&self) -> Result<bool, Error> {
596        let mut statement = self.connection.prepare_cached(SQL_EXIST_KEYS)?;
597        let any_elements: bool = statement.query_one([], |row| row.get(0))?;
598        Ok(!any_elements)
599    }
600
601    /// Removes *all* keys from the map.
602    #[inline]
603    pub fn clear(&mut self) -> Result<(), Error> {
604        let mut statement = self.connection.prepare_cached(SQL_DELETE_ALL)?;
605        statement.execute([])?;
606        Ok(())
607    }
608
609    /// Creates a new [`SQLiteSet`] that contains *all* keys contained in this map.
610    ///
611    /// The created set is case-sensitive.
612    #[inline]
613    pub fn to_set(&mut self) -> Result<SQLiteSet<K>, Error> {
614        let mut key_set = SQLiteSet::<K>::new()?;
615        key_set.transaction()?.insert_mapped_keys(self)?;
616        Ok(key_set)
617    }
618
619    /// Creates a new [`SQLiteSet`] that contains *all* keys contained in this map.
620    ///
621    /// The created set is case-insensitive.
622    ///
623    /// Only ASCII characters are case folded. SQLite does **not** attempt to do full UTF case folding!
624    #[inline]
625    pub fn to_set_nocase(&mut self) -> Result<SQLiteSet<K>, Error>
626    where
627        K: CharType,
628    {
629        let mut key_set = SQLiteSet::<K>::with_nocase()?;
630        key_set.transaction()?.insert_mapped_keys(self)?;
631        Ok(key_set)
632    }
633
634    #[cfg(feature = "backup")]
635    #[inline]
636    /// Stores all contents of this map in the specified database file.
637    ///
638    /// The file will be replaced, if it already exists!
639    pub fn to_file<P>(&self, path: P) -> Result<(), Error>
640    where
641        P: AsRef<Path>,
642    {
643        let mut connection_out = Self::initialize_connection(Connection::open_with_flags(path, *OPEN_FLAGS)?)?;
644        crate::common::backup_db(&self.connection, &mut connection_out)?;
645        Ok(())
646    }
647}
648
649cfg_if! {
650    if #[cfg(feature = "backup")] {
651        impl<K, V> TryClone for SQLiteMap<K, V>
652        where
653            K: Type,
654            V: Type,
655        {
656            type Error = Error;
657
658            /// Creates an exact, independent *copy* of the given map instance.
659            ///
660            /// This function fails with [`Error::Unsupported`](crate::Error::Unsupported), if the optional [`backup`](crate#optional-features) feature is **not** enabled &#128549;
661            #[inline]
662            fn try_clone(&self) -> Result<Self, Self::Error> {
663                let mut connection_cloned = Self::initialize_connection(Connection::open_in_memory()?)?;
664                crate::common::backup_db(&self.connection, &mut connection_cloned)?;
665                Self::from_connection(connection_cloned)
666            }
667        }
668    } else {
669        impl<K, V> TryClone for SQLiteMap<K, V>
670        where
671            K: Type,
672            V: Type,
673        {
674            type Error = Error;
675
676            /// Creates an exact, independent *copy* of the given map instance.
677            ///
678            /// This function fails with [`Error::Unsupported`](crate::Error::Unsupported), if the optional [`backup`](crate#optional-features) feature is **not** enabled &#128549;
679            #[inline]
680            fn try_clone(&self) -> Result<Self, Self::Error> {
681                Err(Error::Unsupported)
682            }
683        }
684    }
685}
686
687// ---------------------------------------------------------------------------
688// SQLiteMap Transaction
689// ---------------------------------------------------------------------------
690
691/// Represents an active SQLite transaction for a [`SQLiteMap`].
692///
693/// Most functions provided by this struct mirror the corresponding functions of the `SQLiteMap` struct.
694///
695/// The transaction is committed when the `SQLiteMapTransaction` is dropped.
696pub struct SQLiteMapTransaction<'a, K, V>
697where
698    K: Type,
699    V: Type,
700{
701    _types: (PhantomData<K>, PhantomData<V>),
702    transaction: Transaction<'a>,
703    last_action: &'a LastActionHolder,
704}
705
706impl<'a, K, V> SQLiteMapTransaction<'a, K, V>
707where
708    K: Type,
709    V: Type,
710{
711    #[inline]
712    fn from(connection: &'a mut Connection, last_action: &'a LastActionHolder) -> Result<Self, Error> {
713        let mut transaction = connection.transaction()?;
714        transaction.set_drop_behavior(rusqlite::DropBehavior::Commit);
715        Ok(Self { _types: (PhantomData, PhantomData), transaction, last_action })
716    }
717
718    /// Drops the `SQLiteMapTransaction`, thereby committing the SQLite transaction.
719    #[inline]
720    pub fn commit(self) {}
721
722    /// This function is equivalent to [`SQLiteMap::insert()`].
723    #[inline]
724    pub fn insert<Q, R>(&mut self, key: &Q, value: &R) -> Result<bool, Error>
725    where
726        K: Borrow<Q>,
727        Q: ?Sized + ToSql,
728        V: Borrow<R>,
729        R: ?Sized + ToSql,
730    {
731        let mut statement = self.transaction.prepare_cached(SQL_INSERT_KEY)?;
732        match statement.execute(params![key, value]) {
733            Ok(_) => Ok(true),
734            Err(error) => check_constraint_violation(error),
735        }
736    }
737
738    /// This function is equivalent to [`SQLiteMap::update()`].
739    #[inline]
740    pub fn update<Q, R>(&mut self, key: &Q, value: &R) -> Result<Action, Error>
741    where
742        K: Borrow<Q>,
743        Q: ?Sized + ToSql,
744        V: Borrow<R>,
745        R: ?Sized + ToSql,
746    {
747        let mut statement = self.transaction.prepare_cached(SQL_UPSERT_KEY)?;
748        statement.execute(params![key, value])?;
749        Ok(Action::from(self.last_action))
750    }
751
752    /// This function is equivalent to [`SQLiteMap::contains_key()`].
753    #[inline]
754    pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
755    where
756        K: Borrow<Q>,
757        Q: ?Sized + ToSql,
758    {
759        let mut statement = self.transaction.prepare_cached(SQL_EXISTS_KEY)?;
760        Ok(statement.exists([key])?)
761    }
762
763    /// This function is equivalent to [`SQLiteMap::contains_key_nocase()`].
764    #[inline]
765    pub fn contains_key_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
766    where
767        K: Borrow<Q> + CharType,
768        Q: ?Sized + ToSql,
769    {
770        let mut statement = self.transaction.prepare_cached(SQL_EXISTS_NOC)?;
771        Ok(statement.exists([key])?)
772    }
773
774    /// This function is equivalent to [`SQLiteMap::get()`].
775    #[inline]
776    pub fn get<Q>(&self, key: &Q) -> Result<Option<V>, Error>
777    where
778        K: Borrow<Q>,
779        Q: ?Sized + ToSql,
780    {
781        let mut statement = self.transaction.prepare_cached(SQL_LOOKUP_KEY)?;
782        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
783    }
784
785    /// This function is equivalent to [`SQLiteMap::get_nocase()`].
786    #[inline]
787    pub fn get_nocase<Q>(&self, key: &Q) -> Result<Option<V>, Error>
788    where
789        K: Borrow<Q> + CharType,
790        Q: ?Sized + ToSql,
791    {
792        let mut statement = self.transaction.prepare_cached(SQL_LOOKUP_NOC)?;
793        Ok(statement.query_one([key], |row| row.get(0)).optional()?)
794    }
795
796    /// This function is equivalent to [`SQLiteMap::remove()`].
797    #[inline]
798    pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
799    where
800        K: Borrow<Q>,
801        Q: ?Sized + ToSql,
802    {
803        let mut statement = self.transaction.prepare_cached(SQL_DELETE_KEY)?;
804        Ok(statement.execute([key])? != 0)
805    }
806
807    /// This function is equivalent to [`SQLiteMap::remove_nocase()`].
808    #[inline]
809    pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
810    where
811        K: Borrow<Q> + CharType,
812        Q: ?Sized + ToSql,
813    {
814        let mut statement = self.transaction.prepare_cached(SQL_DELETE_NOC)?;
815        Ok(statement.execute([key])? != 0)
816    }
817
818    /// This function is equivalent to [`SQLiteMap::for_each_key()`].
819    #[inline]
820    pub fn for_each_key<F>(&self, mut callback: F) -> Result<(), Error>
821    where
822        F: FnMut(K),
823    {
824        let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
825        for key in statement.query_map([], |row| row.get(0))? {
826            callback(key?);
827        }
828        Ok(())
829    }
830
831    /// This function is equivalent to [`SQLiteMap::for_each_entry()`].
832    #[inline]
833    pub fn for_each_entry<F>(&self, mut callback: F) -> Result<(), Error>
834    where
835        F: FnMut(K, V),
836    {
837        let mut statement = self.transaction.prepare_cached(SQL_QUERY_PAIR)?;
838        let mut result = statement.query([])?;
839        while let Some(key_value) = result.next()? {
840            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
841            callback(key, value);
842        }
843        Ok(())
844    }
845
846    /// This is the *failable* version of the [`for_each_key()`](Self::for_each_key) function.
847    #[inline]
848    pub(crate) fn try_for_each_key<F>(&self, mut callback: F) -> Result<(), Error>
849    where
850        F: FnMut(K) -> Result<(), Error>,
851    {
852        let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
853        for key in statement.query_map([], |row| row.get(0))? {
854            callback(key?)?
855        }
856        Ok(())
857    }
858
859    /// This function is equivalent to [`SQLiteMap::find_key()`].
860    #[inline]
861    pub fn find_key<P>(&self, predicate: P) -> Result<Option<K>, Error>
862    where
863        P: Fn(&K) -> bool,
864    {
865        let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
866        for key in statement.query_map([], |row| row.get(0))? {
867            let key: K = key?;
868            if predicate(key.borrow()) {
869                return Ok(Some(key));
870            }
871        }
872        Ok(None)
873    }
874
875    /// This function is equivalent to [`SQLiteMap::find_entry()`].
876    #[inline]
877    pub fn find_entry<P>(&self, predicate: P) -> Result<Option<(K, V)>, Error>
878    where
879        P: Fn(&K, &V) -> bool,
880    {
881        let mut statement = self.transaction.prepare_cached(SQL_QUERY_PAIR)?;
882        let mut result = statement.query([])?;
883        while let Some(key_value) = result.next()? {
884            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
885            if predicate(&key, &value) {
886                return Ok(Some((key, value)));
887            }
888        }
889        Ok(None)
890    }
891
892    /// This function is equivalent to [`SQLiteMap::glob_keys()`].
893    #[inline]
894    pub fn glob_keys<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
895    where
896        F: FnMut(K),
897        K: Borrow<Q> + CharType,
898        Q: ?Sized + ToSql,
899    {
900        let mut statement = self.transaction.prepare_cached(if invert { SQL_GLOBN_KEYS } else { SQL_GLOBA_KEYS })?;
901        for key in statement.query_map([pattern], |row| row.get(0))? {
902            callback(key?);
903        }
904        Ok(())
905    }
906
907    /// This function is equivalent to [`SQLiteMap::glob_entries()`].
908    #[inline]
909    pub fn glob_entries<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
910    where
911        F: FnMut(K, V),
912        K: Borrow<Q> + CharType,
913        Q: ?Sized + ToSql,
914    {
915        let mut statement = self.transaction.prepare_cached(if invert { SQL_GLOBN_PAIR } else { SQL_GLOBA_PAIR })?;
916        let mut result = statement.query([pattern])?;
917        while let Some(key_value) = result.next()? {
918            let (key, value): (K, V) = (key_value.get(0)?, key_value.get(1)?);
919            callback(key, value);
920        }
921        Ok(())
922    }
923
924    /// This function is equivalent to [`SQLiteMap::len()`].
925    #[inline]
926    pub fn len(&self) -> Result<Size, Error> {
927        let mut statement = self.transaction.prepare_cached(SQL_COUNT_KEYS)?;
928        let count: i64 = statement.query_one([], |row| row.get(0))?;
929        Ok(Size::from_i64(count))
930    }
931
932    /// This function is equivalent to [`SQLiteMap::is_empty()`].
933    #[inline]
934    pub fn is_empty(&self) -> Result<bool, Error> {
935        let mut statement = self.transaction.prepare_cached(SQL_EXIST_KEYS)?;
936        let any_elements: bool = statement.query_one([], |row| row.get(0))?;
937        Ok(!any_elements)
938    }
939
940    /// This function is equivalent to [`SQLiteMap::clear()`].
941    #[inline]
942    pub fn clear(&mut self) -> Result<(), Error> {
943        let mut statement = self.transaction.prepare_cached(SQL_DELETE_ALL)?;
944        statement.execute([])?;
945        Ok(())
946    }
947}
948
949// ---------------------------------------------------------------------------
950// Auxilary Functions
951// ---------------------------------------------------------------------------
952
953#[cfg(feature = "hooks")]
954#[inline(always)]
955fn update_counter(counter: &mut (Size, Size), action: Action) {
956    match action {
957        Action::Insert => counter.0 = counter.0.saturating_add(1),
958        Action::Update => counter.1 = counter.1.saturating_add(1),
959        Action::Unknown => (), /*hint::cold_path()*/
960    }
961}
962
963// ---------------------------------------------------------------------------
964// Tests
965// ---------------------------------------------------------------------------
966
967#[cfg(test)]
968mod test {
969    use super::*;
970
971    #[cfg(feature = "hooks")]
972    #[test]
973    fn test_actions() {
974        let action_holder = LastActionHolder::default();
975        for value in [RusqliteAction::SQLITE_INSERT, RusqliteAction::SQLITE_UPDATE, RusqliteAction::UNKNOWN] {
976            action_holder.0.store(value as i32, Relaxed);
977            match Action::from(&action_holder) {
978                Action::Insert => assert_eq!(value, RusqliteAction::SQLITE_INSERT),
979                Action::Update => assert_eq!(value, RusqliteAction::SQLITE_UPDATE),
980                Action::Unknown => {
981                    assert_ne!(value, RusqliteAction::SQLITE_INSERT);
982                    assert_ne!(value, RusqliteAction::SQLITE_UPDATE);
983                }
984            }
985        }
986    }
987
988    #[cfg(not(feature = "hooks"))]
989    #[test]
990    fn test_actions() {
991        let action_holder = LastActionHolder::default();
992        assert!(matches!(Action::from(&action_holder), Action::Unknown));
993    }
994
995    #[cfg(feature = "hooks")]
996    #[test]
997    fn test_counter() {
998        let mut counter = Default::default();
999        for i in 0..8 {
1000            update_counter(&mut counter, Action::Unknown);
1001            assert_eq!(counter, (i, i));
1002            update_counter(&mut counter, Action::Insert);
1003            assert_eq!(counter, (i + 1, i));
1004            update_counter(&mut counter, Action::Update);
1005            assert_eq!(counter, (i + 1, i + 1));
1006        }
1007    }
1008}