1use 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#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub enum Action {
21 Unknown,
25 Insert,
27 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
77const 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
132pub 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 #[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 #[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 #[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 #[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 #[inline]
290 pub fn transaction(&mut self) -> Result<SQLiteMapTransaction<'_, K, V>, Error> {
291 SQLiteMapTransaction::from(&mut self.connection, &self.last_action)
292 }
293
294 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[inline]
680 fn try_clone(&self) -> Result<Self, Self::Error> {
681 Err(Error::Unsupported)
682 }
683 }
684 }
685}
686
687pub 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 #[inline]
720 pub fn commit(self) {}
721
722 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 => (), }
961}
962
963#[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}