1use crate::{
6 CharType, Error, SQLiteMap, 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, ToSql, Transaction},
9};
10use cfg_if::cfg_if;
11use const_format::concatcp;
12use std::{borrow::Borrow, collections::BTreeMap, marker::PhantomData, path::Path, sync::LazyLock};
13
14const TABLE_NAME: &str = "rusqlite_set_v2";
19
20static SQL_CREATE_TBL: LazyLock<BTreeMap<SqlType, String>> = LazyLock::new(|| {
21 let mut map = BTreeMap::<SqlType, String>::new();
22 for sql_type in SQL_TYPES {
23 cfg_if! {
24 if #[cfg(feature = "with_rowid")] {
25 map.insert(sql_type, format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key {sql_type} PRIMARY KEY NOT NULL);"));
26 } else {
27 map.insert(sql_type, format!("CREATE TABLE IF NOT EXISTS {TABLE_NAME} (key {sql_type} PRIMARY KEY NOT NULL) WITHOUT ROWID;"));
28 }
29 }
30 }
31 map
32});
33
34cfg_if! {
35 if #[cfg(feature = "with_rowid")] {
36 const SQL_CREATE_NOC: &str = concatcp!("CREATE TABLE IF NOT EXISTS ", TABLE_NAME, " (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE);");
37 } else {
38 const SQL_CREATE_NOC: &str = concatcp!("CREATE TABLE IF NOT EXISTS ", TABLE_NAME, " (key TEXT PRIMARY KEY NOT NULL COLLATE NOCASE) WITHOUT ROWID;");
39 }
40}
41
42const SQL_GET_SCHEMA: &str = concatcp!("SELECT sql FROM sqlite_schema WHERE type = 'table' AND name = '", TABLE_NAME, "';");
43const SQL_COUNT_KEYS: &str = concatcp!("SELECT COUNT(*) FROM ", TABLE_NAME, ";");
44const SQL_EXIST_KEYS: &str = concatcp!("SELECT EXISTS (SELECT 1 FROM ", TABLE_NAME, ");");
45const SQL_INSERT_KEY: &str = concatcp!("INSERT INTO ", TABLE_NAME, " (key) VALUES (?1);");
46const SQL_EXISTS_KEY: &str = concatcp!("SELECT 1 FROM ", TABLE_NAME, " WHERE key = ?1 LIMIT 1;");
47const SQL_EXISTS_NOC: &str = concatcp!("SELECT 1 FROM ", TABLE_NAME, " WHERE key COLLATE NOCASE = ? LIMIT 1;");
48const SQL_QUERY_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, ";");
49const SQL_GLOBA_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, " WHERE key GLOB ?1;");
50const SQL_GLOBN_KEYS: &str = concatcp!("SELECT key FROM ", TABLE_NAME, " WHERE key NOT GLOB ?1;");
51const SQL_DELETE_KEY: &str = concatcp!("DELETE FROM ", TABLE_NAME, " WHERE key = ?1;");
52const SQL_DELETE_NOC: &str = concatcp!("DELETE FROM ", TABLE_NAME, " WHERE key COLLATE NOCASE = ?1;");
53const SQL_DELETE_ALL: &str = concatcp!("DELETE FROM ", TABLE_NAME, ";");
54
55pub struct SQLiteSet<T>
73where
74 T: Type,
75{
76 _type: PhantomData<T>,
77 connection: Connection,
78}
79
80impl<T> SQLiteSet<T>
81where
82 T: Type,
83{
84 #[inline]
86 pub fn new() -> Result<Self, Error> {
87 Self::from_connection(Self::create_connection::<false, &Path>(None, false, false)?)
88 }
89
90 #[inline]
94 pub fn with_nocase() -> Result<Self, Error>
95 where
96 T: CharType,
97 {
98 Self::from_connection(Self::create_connection::<true, &Path>(None, false, false)?)
99 }
100
101 #[inline]
109 pub fn from_file<P>(path: P, readonly: bool, can_create: bool) -> Result<Self, Error>
110 where
111 P: AsRef<Path>,
112 {
113 Self::from_connection(Self::create_connection::<false, _>(Some(path), readonly, can_create)?)
114 }
115
116 #[inline]
126 pub fn from_file_nocase<P>(path: P, readonly: bool, can_create: bool) -> Result<Self, Error>
127 where
128 T: CharType,
129 P: AsRef<Path>,
130 {
131 Self::from_connection(Self::create_connection::<true, _>(Some(path), readonly, can_create)?)
132 }
133
134 #[inline(always)]
135 fn create_connection<const NO_CASE: bool, P>(path_option: Option<P>, readonly: bool, can_create: bool) -> Result<Connection, Error>
136 where
137 P: AsRef<Path>,
138 {
139 let connection = if let Some(path) = path_option.as_ref() {
140 Connection::open_with_flags(path, if !readonly { if can_create { *OPEN_FLAGS } else { *OPEN_FLAGS_NOCREATE } } else { *OPEN_FLAGS_RDONLY })?
141 } else {
142 Connection::open_in_memory()?
143 };
144
145 let connection = Self::initialize_connection(connection)?;
146
147 #[cfg(feature = "checked")]
148 if path_option.is_some() {
149 crate::common::integrity_check(&connection)?;
150 }
151
152 let sql_create_table = if !NO_CASE {
153 SQL_CREATE_TBL.get(&T::SQL_TYPE).unwrap()
154 } else {
155 debug_assert_eq!(T::SQL_TYPE, SQL_TYPE_TEXT);
156 SQL_CREATE_NOC
157 };
158
159 connection.execute(sql_create_table, [])?;
160
161 if path_option.is_some() {
162 let sql_schema_table: String = connection.query_one(SQL_GET_SCHEMA, [], |row| row.get(0))?;
163 if !verify_schema(sql_create_table, &sql_schema_table) {
164 return Err(Error::InvalidSchema);
165 }
166 }
167
168 Ok(connection)
169 }
170
171 #[inline(always)]
172 fn initialize_connection(connection: Connection) -> Result<Connection, Error> {
173 connection.pragma_update(None, "synchronous", "OFF")?;
174 connection.pragma_update(None, "journal_mode", "OFF")?;
175 Ok(connection)
176 }
177
178 #[inline(always)]
179 fn from_connection(connection: Connection) -> Result<Self, Error> {
180 Ok(Self { _type: PhantomData, connection })
181 }
182
183 #[inline]
189 pub fn transaction(&mut self) -> Result<SQLiteSetTransaction<'_, T>, Error> {
190 SQLiteSetTransaction::from(&mut self.connection)
191 }
192
193 #[inline]
197 pub fn insert<Q>(&mut self, key: &Q) -> Result<bool, Error>
198 where
199 T: Borrow<Q>,
200 Q: ?Sized + ToSql,
201 {
202 let mut statement = self.connection.prepare_cached(SQL_INSERT_KEY)?;
203 match statement.execute([key]) {
204 Ok(_) => Ok(true),
205 Err(error) => check_constraint_violation(error),
206 }
207 }
208
209 #[inline]
213 pub fn union(&mut self, other: &Self) -> Result<Size, Error> {
214 let trx = self.transaction()?;
215 let mut statement_insert = trx.transaction.prepare_cached(SQL_INSERT_KEY)?;
216 let mut statement_query = other.connection.prepare_cached(SQL_QUERY_KEYS)?;
217 let mut counter = Size::default();
218 for key in statement_query.query_map([], |row| row.get::<_, T>(0))? {
219 if match statement_insert.execute([key?]) {
220 Ok(_) => true,
221 Err(error) => check_constraint_violation(error)?,
222 } {
223 counter += 1;
224 }
225 }
226 Ok(counter)
227 }
228
229 #[inline]
235 pub fn contains<Q>(&self, key: &Q) -> Result<bool, Error>
236 where
237 T: Borrow<Q>,
238 Q: ?Sized + ToSql,
239 {
240 let mut statement = self.connection.prepare_cached(SQL_EXISTS_KEY)?;
241 Ok(statement.exists([key])?)
242 }
243
244 #[inline]
250 pub fn contains_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
251 where
252 T: Borrow<Q> + CharType,
253 Q: ?Sized + ToSql,
254 {
255 let mut statement = self.connection.prepare_cached(SQL_EXISTS_NOC)?;
256 Ok(statement.exists([key])?)
257 }
258
259 #[inline]
265 pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
266 where
267 T: Borrow<Q>,
268 Q: ?Sized + ToSql,
269 {
270 let mut statement = self.connection.prepare_cached(SQL_DELETE_KEY)?;
271 Ok(statement.execute([key])? != 0)
272 }
273
274 #[inline]
280 pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
281 where
282 T: Borrow<Q> + CharType,
283 Q: ?Sized + ToSql,
284 {
285 let mut statement = self.connection.prepare_cached(SQL_DELETE_NOC)?;
286 Ok(statement.execute([key])? != 0)
287 }
288
289 #[inline]
293 pub fn for_each<F>(&self, mut callback: F) -> Result<(), Error>
294 where
295 F: FnMut(T),
296 {
297 let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
298 for key in statement.query_map([], |row| row.get(0))? {
299 callback(key?);
300 }
301 Ok(())
302 }
303
304 #[inline]
312 pub fn find<P, Q>(&self, predicate: P) -> Result<Option<T>, Error>
313 where
314 P: Fn(&Q) -> bool,
315 T: Borrow<Q>,
316 Q: ?Sized,
317 {
318 let mut statement = self.connection.prepare_cached(SQL_QUERY_KEYS)?;
319 for key in statement.query_map([], |row| row.get(0))? {
320 let key: T = key?;
321 if predicate(key.borrow()) {
322 return Ok(Some(key));
323 }
324 }
325 Ok(None)
326 }
327
328 #[inline]
336 pub fn glob<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
337 where
338 F: FnMut(T),
339 T: Borrow<Q> + CharType,
340 Q: ?Sized + ToSql,
341 {
342 let mut statement = self.connection.prepare_cached(if invert { SQL_GLOBN_KEYS } else { SQL_GLOBA_KEYS })?;
343 for key in statement.query_map([pattern], |row| row.get(0))? {
344 callback(key?);
345 }
346 Ok(())
347 }
348
349 #[inline]
351 pub fn len(&self) -> Result<Size, Error> {
352 let mut statement = self.connection.prepare_cached(SQL_COUNT_KEYS)?;
353 let count: i64 = statement.query_one([], |row| row.get(0))?;
354 Ok(Size::from_i64(count))
355 }
356
357 #[inline]
359 pub fn is_empty(&self) -> Result<bool, Error> {
360 let mut statement = self.connection.prepare_cached(SQL_EXIST_KEYS)?;
361 let any_elements: bool = statement.query_one([], |row| row.get(0))?;
362 Ok(!any_elements)
363 }
364
365 #[inline]
367 pub fn clear(&mut self) -> Result<(), Error> {
368 let mut statement = self.connection.prepare_cached(SQL_DELETE_ALL)?;
369 statement.execute([])?;
370 Ok(())
371 }
372
373 #[cfg(feature = "backup")]
374 #[inline]
375 pub fn to_file<P>(&self, path: P) -> Result<(), Error>
379 where
380 P: AsRef<Path>,
381 {
382 let mut connection_out = Self::initialize_connection(Connection::open_with_flags(path, *OPEN_FLAGS)?)?;
383 crate::common::backup_db(&self.connection, &mut connection_out)?;
384 Ok(())
385 }
386}
387
388cfg_if! {
389 if #[cfg(feature = "backup")] {
390 impl<T> TryClone for SQLiteSet<T>
391 where
392 T: Type,
393 {
394 type Error = Error;
395
396 #[inline]
400 fn try_clone(&self) -> Result<Self, Self::Error> {
401 let mut connection_cloned = Self::initialize_connection(Connection::open_in_memory()?)?;
402 crate::common::backup_db(&self.connection, &mut connection_cloned)?;
403 Self::from_connection(connection_cloned)
404 }
405 }
406 } else {
407 impl<T> TryClone for SQLiteSet<T>
408 where
409 T: Type,
410 {
411 type Error = Error;
412
413 #[inline]
417 fn try_clone(&self) -> Result<Self, Self::Error> {
418 Err(Error::Unsupported)
419 }
420 }
421 }
422}
423
424pub struct SQLiteSetTransaction<'a, T>
434where
435 T: Type,
436{
437 _type: PhantomData<T>,
438 transaction: Transaction<'a>,
439}
440
441impl<'a, T> SQLiteSetTransaction<'a, T>
442where
443 T: Type,
444{
445 #[inline]
446 fn from(connection: &'a mut Connection) -> Result<Self, Error> {
447 let mut transaction = connection.transaction()?;
448 transaction.set_drop_behavior(rusqlite::DropBehavior::Commit);
449 Ok(Self { _type: PhantomData, transaction })
450 }
451
452 #[inline]
454 pub fn commit(self) {}
455
456 #[inline]
458 pub fn insert<Q>(&mut self, key: &Q) -> Result<bool, Error>
459 where
460 T: Borrow<Q>,
461 Q: ?Sized + ToSql,
462 {
463 let mut statement = self.transaction.prepare_cached(SQL_INSERT_KEY)?;
464 match statement.execute([key]) {
465 Ok(_) => Ok(true),
466 Err(error) => check_constraint_violation(error),
467 }
468 }
469
470 #[inline]
472 pub(crate) fn insert_mapped_keys<Q>(&mut self, map: &mut SQLiteMap<T, Q>) -> Result<(), Error>
473 where
474 Q: Type,
475 {
476 let mut statement = self.transaction.prepare_cached(SQL_INSERT_KEY)?;
477 let trx = map.transaction()?;
478 trx.try_for_each_key(|key| match statement.execute([key]) {
479 Ok(_) => Ok(()),
480 Err(error) => check_constraint_violation(error).map(|_| ()),
481 })
482 }
483
484 #[inline]
486 pub fn contains<Q>(&self, key: &Q) -> Result<bool, Error>
487 where
488 T: Borrow<Q>,
489 Q: ?Sized + ToSql,
490 {
491 let mut statement = self.transaction.prepare_cached(SQL_EXISTS_KEY)?;
492 Ok(statement.exists([key])?)
493 }
494
495 #[inline]
497 pub fn contains_nocase<Q>(&self, key: &Q) -> Result<bool, Error>
498 where
499 T: Borrow<Q> + CharType,
500 Q: ?Sized + ToSql,
501 {
502 let mut statement = self.transaction.prepare_cached(SQL_EXISTS_NOC)?;
503 Ok(statement.exists([key])?)
504 }
505
506 #[inline]
508 pub fn remove<Q>(&mut self, key: &Q) -> Result<bool, Error>
509 where
510 T: Borrow<Q>,
511 Q: ?Sized + ToSql,
512 {
513 let mut statement = self.transaction.prepare_cached(SQL_DELETE_KEY)?;
514 Ok(statement.execute([key])? != 0)
515 }
516
517 #[inline]
519 pub fn remove_nocase<Q>(&mut self, key: &Q) -> Result<bool, Error>
520 where
521 T: Borrow<Q> + CharType,
522 Q: ?Sized + ToSql,
523 {
524 let mut statement = self.transaction.prepare_cached(SQL_DELETE_NOC)?;
525 Ok(statement.execute([key])? != 0)
526 }
527
528 #[inline]
530 pub fn for_each<F>(&self, mut callback: F) -> Result<(), Error>
531 where
532 F: FnMut(T),
533 {
534 let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
535 for key in statement.query_map([], |row| row.get(0))? {
536 callback(key?);
537 }
538 Ok(())
539 }
540
541 #[inline]
543 pub fn find<P, Q>(&self, predicate: P) -> Result<Option<T>, Error>
544 where
545 P: Fn(&Q) -> bool,
546 T: Borrow<Q>,
547 Q: ?Sized,
548 {
549 let mut statement = self.transaction.prepare_cached(SQL_QUERY_KEYS)?;
550 for key in statement.query_map([], |row| row.get(0))? {
551 let key: T = key?;
552 if predicate(key.borrow()) {
553 return Ok(Some(key));
554 }
555 }
556 Ok(None)
557 }
558
559 #[inline]
561 pub fn glob<F, Q>(&self, invert: bool, pattern: &Q, mut callback: F) -> Result<(), Error>
562 where
563 F: FnMut(T),
564 T: Borrow<Q> + CharType,
565 Q: ?Sized + ToSql,
566 {
567 let mut statement = self.transaction.prepare_cached(if invert { SQL_GLOBN_KEYS } else { SQL_GLOBA_KEYS })?;
568 for key in statement.query_map([pattern], |row| row.get(0))? {
569 callback(key?);
570 }
571 Ok(())
572 }
573
574 #[inline]
576 pub fn len(&self) -> Result<Size, Error> {
577 let mut statement = self.transaction.prepare_cached(SQL_COUNT_KEYS)?;
578 let count: i64 = statement.query_one([], |row| row.get(0))?;
579 Ok(Size::from_i64(count))
580 }
581
582 #[inline]
584 pub fn is_empty(&self) -> Result<bool, Error> {
585 let mut statement = self.transaction.prepare_cached(SQL_EXIST_KEYS)?;
586 let any_elements: bool = statement.query_one([], |row| row.get(0))?;
587 Ok(!any_elements)
588 }
589
590 #[inline]
592 pub fn clear(&mut self) -> Result<(), Error> {
593 let mut statement = self.transaction.prepare_cached(SQL_DELETE_ALL)?;
594 statement.execute([])?;
595 Ok(())
596 }
597}