1#![deny(clippy::all)]
2
3mod tools;
4
5pub use async_sqlite::rusqlite::OpenFlags;
6use async_sqlite::rusqlite::{params, OptionalExtension as _};
7use async_sqlite::*;
8use keyvaluedb::{
9 DBKeyRef, DBKeyValue, DBKeyValueRef, DBOp, DBTransaction, DBTransactionError, DBValue, IoStats,
10 IoStatsKind, KeyValueDB, KeyValueDBPinBoxFuture,
11};
12use parking_lot::{Mutex, RwLock};
13use std::sync::atomic::{AtomicBool, Ordering};
14use std::sync::Arc;
15use std::time::{Duration, Instant};
16use std::{
17 io,
18 path::{Path, PathBuf},
19 str::FromStr,
20};
21use tools::*;
22
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
26pub enum VacuumMode {
27 None,
28 Incremental,
29 Full,
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34pub enum RepairCheck {
35 None,
38 Quick,
41 Full,
44}
45
46pub type RepairCallback = Arc<dyn Fn(&RepairReport) + Send + Sync>;
48
49#[derive(Clone)]
51pub struct DatabaseConfig {
52 pub columns: u32,
55 pub flags: OpenFlags,
57 pub num_conns: usize,
59 pub vacuum_mode: VacuumMode,
61 pub repair_on_corrupt: bool,
64 pub repair_check: RepairCheck,
66 pub repair_check_byte_limit: Option<u64>,
71 pub on_repair: Option<RepairCallback>,
73}
74
75impl DatabaseConfig {
76 pub fn new() -> Self {
78 Default::default()
79 }
80
81 pub fn with_columns(self, columns: u32) -> Self {
83 assert!(columns > 0, "the number of columns must not be zero");
84 Self { columns, ..self }
85 }
86
87 pub fn with_repair_on_corrupt(self, repair_on_corrupt: bool) -> Self {
89 Self {
90 repair_on_corrupt,
91 ..self
92 }
93 }
94
95 pub fn with_repair_check(self, repair_check: RepairCheck) -> Self {
97 Self {
98 repair_check,
99 ..self
100 }
101 }
102
103 pub fn with_repair_check_byte_limit(self, bytes: u64) -> Self {
105 Self {
106 repair_check_byte_limit: Some(bytes),
107 ..self
108 }
109 }
110
111 pub fn with_on_repair(self, on_repair: RepairCallback) -> Self {
113 Self {
114 on_repair: Some(on_repair),
115 ..self
116 }
117 }
118
119 pub fn with_in_memory(self) -> Self {
121 Self {
122 flags: OpenFlags::SQLITE_OPEN_READ_WRITE
123 | OpenFlags::SQLITE_OPEN_CREATE
124 | OpenFlags::SQLITE_OPEN_NO_MUTEX
125 | OpenFlags::SQLITE_OPEN_MEMORY,
126 ..self
127 }
128 }
129
130 pub fn with_flags(self, flags: OpenFlags) -> Self {
132 Self { flags, ..self }
133 }
134
135 pub fn with_num_conns(self, num_conns: usize) -> Self {
137 Self { num_conns, ..self }
138 }
139
140 pub fn with_vacuum_mode(self, vacuum_mode: VacuumMode) -> Self {
142 Self {
143 vacuum_mode,
144 ..self
145 }
146 }
147}
148
149impl Default for DatabaseConfig {
150 fn default() -> DatabaseConfig {
151 DatabaseConfig {
152 columns: 1,
153 flags: OpenFlags::SQLITE_OPEN_READ_WRITE
154 | OpenFlags::SQLITE_OPEN_CREATE
155 | OpenFlags::SQLITE_OPEN_NO_MUTEX,
156 num_conns: 1,
157 vacuum_mode: VacuumMode::None,
158 repair_on_corrupt: false,
159 repair_check: RepairCheck::Full,
160 repair_check_byte_limit: None,
161 on_repair: None,
162 }
163 }
164}
165
166fn code_is_corruption(e: &rusqlite::Error) -> bool {
170 matches!(
171 e,
172 rusqlite::Error::SqliteFailure(f, _) if matches!(
173 f.code,
174 rusqlite::ErrorCode::DatabaseCorrupt | rusqlite::ErrorCode::NotADatabase
175 )
176 )
177}
178
179fn error_is_corruption(e: &Error) -> bool {
181 matches!(e, Error::Rusqlite(e) if code_is_corruption(e))
182}
183
184fn io_error_is_corruption(e: &io::Error) -> bool {
186 let mut src: Option<&(dyn std::error::Error + 'static)> = e.get_ref().map(|b| b as _);
187 while let Some(cur) = src {
188 if let Some(e) = cur.downcast_ref::<rusqlite::Error>() {
189 return code_is_corruption(e);
190 }
191 if let Some(Error::Rusqlite(e)) = cur.downcast_ref::<Error>() {
192 return code_is_corruption(e);
193 }
194 src = cur.source();
195 }
196 false
197}
198
199pub struct DatabaseTable {
203 _table: String,
204 str_has_value: String,
205 str_has_value_like: String,
206 str_get_unique_value: String,
207 str_get_first_value_like: String,
208 str_set_unique_value: String,
209 str_remove_unique_value: String,
210 str_remove_and_return_unique_value: String,
211 str_remove_unique_value_like: String,
212 str_iter_with_prefix: String,
213 str_iter_no_prefix: String,
214 str_iter_keys_with_prefix: String,
215 str_iter_keys_no_prefix: String,
216}
217
218impl DatabaseTable {
219 pub fn new(table: String) -> Self {
220 let str_has_value = format!("SELECT 1 FROM {} WHERE [key] = ? LIMIT 1", table);
221 let str_has_value_like = format!(
222 "SELECT 1 FROM {} WHERE [key] LIKE ? ESCAPE '\\' LIMIT 1",
223 table
224 );
225 let str_get_unique_value = format!("SELECT value FROM {} WHERE [key] = ? LIMIT 1", table);
226 let str_get_first_value_like = format!(
227 "SELECT key, value FROM {} WHERE [key] LIKE ? ESCAPE '\\' LIMIT 1",
228 table
229 );
230 let str_set_unique_value = format!(
231 "INSERT OR REPLACE INTO {} ([key], value) VALUES(?, ?)",
232 table
233 );
234 let str_remove_unique_value = format!("DELETE FROM {} WHERE [key] = ?", table);
235 let str_remove_and_return_unique_value =
236 format!("DELETE FROM {} WHERE [key] = ? RETURNING value", table);
237 let str_remove_unique_value_like =
238 format!("DELETE FROM {} WHERE [key] LIKE ? ESCAPE '\\'", table);
239 let str_iter_with_prefix = format!(
240 "SELECT key, value FROM {} WHERE [key] LIKE ? ESCAPE '\\'",
241 table
242 );
243 let str_iter_no_prefix = format!("SELECT key, value FROM {}", table);
244 let str_iter_keys_with_prefix =
245 format!("SELECT key FROM {} WHERE [key] LIKE ? ESCAPE '\\'", table);
246 let str_iter_keys_no_prefix = format!("SELECT key FROM {}", table);
247
248 Self {
249 _table: table,
250 str_has_value,
251 str_has_value_like,
252 str_get_unique_value,
253 str_get_first_value_like,
254 str_set_unique_value,
255 str_remove_unique_value,
256 str_remove_and_return_unique_value,
257 str_remove_unique_value_like,
258 str_iter_with_prefix,
259 str_iter_no_prefix,
260 str_iter_keys_with_prefix,
261 str_iter_keys_no_prefix,
262 }
263 }
264}
265
266#[derive(Debug, Clone)]
270pub struct RepairReport {
271 pub detected: String,
273 pub rows_recovered: u64,
275 pub partial_tables: u32,
277 pub corrupt_path: PathBuf,
279}
280
281struct DatabaseState {
285 pool: Pool,
286 generation: u64,
287 repair_report: Option<RepairReport>,
288}
289
290#[derive(Default)]
292struct Salvage {
293 tables: Vec<(String, Vec<(String, rusqlite::types::Value)>)>,
294 partial_tables: u32,
295}
296
297const SIBLING_SUFFIXES: [&str; 3] = ["", "-wal", "-shm"];
298
299fn sibling(base: &Path, ext: &str, suffix: &str) -> PathBuf {
300 let mut os = base.as_os_str().to_owned();
301 os.push(ext);
302 os.push(suffix);
303 PathBuf::from(os)
304}
305
306fn db_file_bytes(path: &Path) -> u64 {
308 let size = |p: PathBuf| std::fs::metadata(p).map(|m| m.len()).unwrap_or(0);
309 size(sibling(path, "", "")) + size(sibling(path, "", "-wal"))
310}
311
312fn swap_repaired_files(path: &Path) -> PathBuf {
315 let corrupt_path = sibling(path, ".corrupt", "");
316 for suffix in &SIBLING_SUFFIXES {
317 let _ = std::fs::remove_file(sibling(path, ".corrupt", suffix));
318 let _ = std::fs::rename(sibling(path, "", suffix), sibling(path, ".corrupt", suffix));
319 let _ = std::fs::rename(
320 sibling(path, ".repairing", suffix),
321 sibling(path, "", suffix),
322 );
323 }
324 corrupt_path
325}
326
327pub struct DatabaseUnlockedInner {
329 path: PathBuf,
330 config: DatabaseConfig,
331 state: RwLock<DatabaseState>,
332 control_table: Arc<DatabaseTable>,
333 column_tables: Vec<Arc<DatabaseTable>>,
334}
335
336impl Drop for DatabaseUnlockedInner {
337 fn drop(&mut self) {
338 let _ = self.state.get_mut().pool.close_blocking();
339 }
340}
341
342pub struct DatabaseInner {
343 overall_stats: IoStats,
344 current_stats: IoStats,
345}
346
347#[derive(Clone)]
348pub struct Database {
349 unlocked_inner: Arc<DatabaseUnlockedInner>,
350 inner: Arc<Mutex<DatabaseInner>>,
351}
352
353impl Database {
354 pub fn open<P: AsRef<Path>>(path: P, config: DatabaseConfig) -> io::Result<Self> {
358 let path = PathBuf::from(path.as_ref());
359 let db = match Self::open_raw(&path, &config, None) {
360 Ok(db) => db,
361 Err(e) if config.repair_on_corrupt && io_error_is_corruption(&e) => {
362 Self::repair(None, &path, &config, e.to_string())?
363 }
364 Err(e) => return Err(e),
365 };
366 let db = if config.repair_on_corrupt {
367 let check = match config.repair_check_byte_limit {
370 Some(limit) if db_file_bytes(&path) > limit => RepairCheck::None,
371 _ => config.repair_check,
372 };
373 match db.check_blocking(check) {
374 Ok(None) => db,
375 Ok(Some(detected)) => Self::repair(Some(db), &path, &config, detected)?,
376 Err(e) if io_error_is_corruption(&e) => {
377 Self::repair(Some(db), &path, &config, e.to_string())?
378 }
379 Err(e) => return Err(e),
380 }
381 } else {
382 db
383 };
384
385 if !db.config().flags.contains(OpenFlags::SQLITE_OPEN_MEMORY) {
388 db.conn_blocking(|conn| conn.pragma_update(None, "wal_checkpoint", "TRUNCATE"))
389 .map_err(io::Error::other)?;
390 }
391
392 if let (Some(on_repair), Some(report)) = (&db.config().on_repair, db.repair_report()) {
393 on_repair(&report);
394 }
395 Ok(db)
396 }
397
398 fn open_pool(path: &Path, config: &DatabaseConfig) -> io::Result<Pool> {
401 let in_memory = config.flags.contains(OpenFlags::SQLITE_OPEN_MEMORY);
402 let mut pool_builder = PoolBuilder::new()
403 .path(path)
404 .flags(config.flags)
405 .num_conns(config.num_conns);
406 if !in_memory {
407 pool_builder = pool_builder.journal_mode(JournalMode::Wal);
408 }
409 let pool = pool_builder.open_blocking().map_err(io::Error::other)?;
410
411 for res in pool.conn_for_each_blocking(move |conn| {
412 conn.set_prepared_statement_cache_capacity(256);
414
415 conn.pragma_update(None, "case_sensitive_like", "ON")?;
416 conn.pragma_update(None, "synchronous", "normal")?;
417 conn.pragma_update(None, "journal_size_limit", 6144000)?;
418 conn.pragma_update(None, "busy_timeout", 2000)?;
420 conn.pragma_update(None, "cell_size_check", "ON")?;
423 Ok(())
424 }) {
425 res.map_err(io::Error::other)?;
426 }
427 Ok(pool)
428 }
429
430 fn open_raw(
431 path: &Path,
432 config: &DatabaseConfig,
433 repair_report: Option<RepairReport>,
434 ) -> io::Result<Self> {
435 let config = config.clone();
436 assert_ne!(config.columns, 0, "number of columns must be >= 1");
437
438 let path = PathBuf::from(path);
439
440 let mut column_tables = vec![];
441 for n in 0..config.columns {
442 column_tables.push(Arc::new(DatabaseTable::new(get_column_table_name(n))))
443 }
444 let control_table = Arc::new(DatabaseTable::new("control".to_string()));
445
446 let pool = Self::open_pool(&path, &config)?;
447
448 let out = Self {
449 unlocked_inner: Arc::new(DatabaseUnlockedInner {
450 path,
451 config,
452 state: RwLock::new(DatabaseState {
453 pool,
454 generation: 0,
455 repair_report,
456 }),
457 control_table,
458 column_tables,
459 }),
460 inner: Arc::new(Mutex::new(DatabaseInner {
461 overall_stats: IoStats::empty(),
462 current_stats: IoStats::empty(),
463 })),
464 };
465
466 let vacuum_mode = out.config().vacuum_mode;
467
468 out.conn_blocking(move |conn| {
469 match vacuum_mode {
470 VacuumMode::None | VacuumMode::Full => {
471 let current: u32 =
472 conn.pragma_query_value(None, "auto_vacuum", |x| x.get(0))?;
473 if current != 0 {
474 conn.execute("VACUUM", [])?;
475 conn.pragma_update(None, "auto_vacuum", 0)?;
476 }
477 }
478 VacuumMode::Incremental => {
479 let current: u32 =
480 conn.pragma_query_value(None, "auto_vacuum", |x| x.get(0))?;
481 if current != 2 {
482 conn.execute("VACUUM", [])?;
483 conn.pragma_update(None, "auto_vacuum", "2")?;
484 }
485 }
486 }
487
488 Ok(())
489 })
490 .map_err(io::Error::other)?;
491
492 out.open_resize_columns()?;
493
494 Ok(out)
495 }
496
497 pub fn path(&self) -> PathBuf {
498 self.unlocked_inner.path.clone()
499 }
500
501 pub fn repair_report(&self) -> Option<RepairReport> {
503 self.unlocked_inner.state.read().repair_report.clone()
504 }
505
506 fn current(&self) -> (Pool, u64) {
509 let state = self.unlocked_inner.state.read();
510 (state.pool.clone(), state.generation)
511 }
512
513 fn repair_enabled(&self) -> bool {
514 self.unlocked_inner.config.repair_on_corrupt
515 }
516
517 fn check_blocking(&self, check: RepairCheck) -> io::Result<Option<String>> {
520 let pragma = match check {
521 RepairCheck::None => return Ok(None),
522 RepairCheck::Quick => "PRAGMA quick_check(8)",
523 RepairCheck::Full => "PRAGMA integrity_check(8)",
524 };
525 let problems = self
526 .conn_blocking(move |conn| {
527 let mut stmt = conn.prepare(pragma)?;
528 let mut rows = stmt.query([])?;
529 let mut problems: Vec<String> = Vec::new();
530 while let Some(row) = rows.next()? {
531 problems.push(row.get(0)?);
532 }
533 Ok(problems)
534 })
535 .map_err(io::Error::other)?;
536 if problems.len() == 1 && problems[0] == "ok" {
537 return Ok(None);
538 }
539 Ok(Some(problems.join("; ")))
540 }
541
542 fn repair(
551 old: Option<Self>,
552 path: &Path,
553 config: &DatabaseConfig,
554 detected: String,
555 ) -> io::Result<Self> {
556 let mut rows_recovered = 0u64;
557 let mut partial_tables = 0u32;
558 if let Some(old) = &old {
559 let (old_pool, _) = old.current();
560 let salvage = Self::salvage_all(&old_pool, config)?;
561 partial_tables = salvage.partial_tables;
562 rows_recovered = Self::build_replacement(path, config, salvage)?;
563 } else {
564 Self::build_replacement(path, config, Salvage::default())?;
565 }
566
567 drop(old);
569 let corrupt_path = swap_repaired_files(path);
570
571 Self::open_raw(
572 path,
573 config,
574 Some(RepairReport {
575 detected,
576 rows_recovered,
577 partial_tables,
578 corrupt_path,
579 }),
580 )
581 }
582
583 fn repair_live(&self, detected: String, seen_generation: u64) -> io::Result<()> {
591 let path = self.unlocked_inner.path.clone();
592 let config = self.unlocked_inner.config.clone();
593 let mut state = self.unlocked_inner.state.write();
594 if state.generation != seen_generation {
595 return Ok(());
596 }
597
598 let salvage = Self::salvage_all(&state.pool, &config)?;
599 let partial_tables = salvage.partial_tables;
600 let rows_recovered = Self::build_replacement(&path, &config, salvage)?;
601
602 if let Err(e) = state.pool.close_blocking() {
603 if let Ok(pool) = Self::open_pool(&path, &config) {
605 state.pool = pool;
606 state.generation += 1;
607 }
608 return Err(io::Error::other(e));
609 }
610 let corrupt_path = swap_repaired_files(&path);
611
612 state.pool = Self::open_pool(&path, &config)?;
613 state.generation += 1;
614 let report = RepairReport {
615 detected,
616 rows_recovered,
617 partial_tables,
618 corrupt_path,
619 };
620 state.repair_report = Some(report.clone());
621 drop(state);
622
623 if let Some(on_repair) = &config.on_repair {
624 on_repair(&report);
625 }
626 Ok(())
627 }
628
629 fn salvage_all(pool: &Pool, config: &DatabaseConfig) -> io::Result<Salvage> {
634 let config_columns = config.columns;
635 pool.conn_mut_blocking(move |conn| {
636 let tx = conn.transaction_with_behavior(rusqlite::TransactionBehavior::Exclusive)?;
637 let columns = tx
640 .query_row(
641 "SELECT value FROM control WHERE [key] = 'columns'",
642 [],
643 |row| row.get::<_, String>(0),
644 )
645 .ok()
646 .and_then(|v| v.parse::<u32>().ok())
647 .unwrap_or(0)
648 .max(config_columns);
649 let mut tables = vec!["control".to_string()];
650 for cn in 0..columns {
651 tables.push(get_column_table_name(cn));
652 }
653 let mut out = Salvage::default();
654 for table in tables {
655 let (rows, partial) = Self::salvage_table_rows(&tx, &table);
656 if partial {
657 out.partial_tables += 1;
658 }
659 out.tables.push((table, rows));
660 }
661 Ok(out)
662 })
663 .map_err(io::Error::other)
664 }
665
666 fn build_replacement(
670 path: &Path,
671 config: &DatabaseConfig,
672 salvage: Salvage,
673 ) -> io::Result<u64> {
674 let tmp = sibling(path, ".repairing", "");
675 for suffix in &SIBLING_SUFFIXES {
676 let _ = std::fs::remove_file(sibling(path, ".repairing", suffix));
677 }
678 let fresh = Self::open_raw(&tmp, config, None)?;
679
680 let mut rows_recovered = 0u64;
681 for (table, rows) in salvage.tables {
682 if rows.is_empty() {
683 continue;
684 }
685 rows_recovered += rows.len() as u64;
686 fresh
689 .conn_blocking(move |conn| {
690 let tx = conn.unchecked_transaction()?;
691 {
692 let mut stmt = tx.prepare(&format!(
693 "INSERT OR IGNORE INTO {} ([key], value) VALUES (?, ?)",
694 table
695 ))?;
696 for (k, v) in &rows {
697 stmt.execute(params![k, v])?;
698 }
699 }
700 tx.commit()
701 })
702 .map_err(io::Error::other)?;
703 }
704 Ok(rows_recovered)
705 }
706
707 fn salvage_table_rows(
710 conn: &rusqlite::Connection,
711 table: &str,
712 ) -> (Vec<(String, rusqlite::types::Value)>, bool) {
713 let mut out = Vec::new();
714 let mut partial = true;
715 if let Ok(mut stmt) = conn.prepare(&format!("SELECT [key], value FROM {}", table)) {
716 if let Ok(mut rows) = stmt.query([]) {
717 loop {
718 match rows.next() {
719 Ok(Some(row)) => {
720 if let (Ok(k), Ok(v)) = (row.get(0), row.get(1)) {
721 out.push((k, v));
722 }
723 }
724 Ok(None) => {
725 partial = false;
726 break;
727 }
728 Err(_) => break,
729 }
730 }
731 }
732 }
733 (out, partial)
734 }
735
736 pub fn config(&self) -> DatabaseConfig {
737 self.unlocked_inner.config.clone()
738 }
739
740 pub fn columns(&self) -> u32 {
741 self.unlocked_inner.config.columns
742 }
743
744 pub fn control_table(&self) -> Arc<DatabaseTable> {
745 self.unlocked_inner.control_table.clone()
746 }
747
748 pub fn column_table(&self, col: u32) -> Arc<DatabaseTable> {
749 self.unlocked_inner.column_tables[col as usize].clone()
750 }
751
752 pub fn conn_blocking<T, F>(&self, func: F) -> Result<T, Error>
753 where
754 F: FnOnce(&rusqlite::Connection) -> Result<T, rusqlite::Error> + Send + 'static,
755 T: Send + 'static,
756 {
757 self.current().0.conn_blocking(func)
758 }
759
760 pub async fn conn<T, F>(&self, func: F) -> Result<T, Error>
761 where
762 F: FnOnce(&rusqlite::Connection) -> Result<T, rusqlite::Error> + Send + 'static,
763 T: Send + 'static,
764 {
765 self.current().0.conn(func).await
766 }
767
768 pub async fn conn_mut<T, F>(&self, func: F) -> Result<T, Error>
769 where
770 F: FnOnce(&mut rusqlite::Connection) -> Result<T, rusqlite::Error> + Send + 'static,
771 T: Send + 'static,
772 {
773 self.current().0.conn_mut(func).await
774 }
775
776 fn recover(&self, e: Error, seen_generation: u64) -> Result<(), Error> {
783 if !self.repair_enabled() {
784 return Err(e);
785 }
786 if error_is_corruption(&e) {
787 if self.repair_live(e.to_string(), seen_generation).is_err() {
788 return Err(e);
789 }
790 return Ok(());
791 }
792 if self.current().1 == seen_generation {
795 return Err(e);
796 }
797 Ok(())
798 }
799
800 async fn conn_retry<T, MK>(&self, make: MK) -> Result<T, Error>
803 where
804 MK: Fn() -> Box<dyn FnOnce(&rusqlite::Connection) -> Result<T, rusqlite::Error> + Send>,
805 T: Send + 'static,
806 {
807 let (pool, generation) = self.current();
808 match pool.conn(make()).await {
809 Err(e) => {
810 self.recover(e, generation)?;
811 self.current().0.conn(make()).await
812 }
813 r => r,
814 }
815 }
816
817 fn conn_retry_blocking<T, MK>(&self, make: MK) -> Result<T, Error>
819 where
820 MK: Fn() -> Box<dyn FnOnce(&rusqlite::Connection) -> Result<T, rusqlite::Error> + Send>,
821 T: Send + 'static,
822 {
823 let (pool, generation) = self.current();
824 match pool.conn_blocking(make()) {
825 Err(e) => {
826 self.recover(e, generation)?;
827 self.current().0.conn_blocking(make())
828 }
829 r => r,
830 }
831 }
832
833 async fn conn_mut_retry<T, MK>(&self, make: MK) -> Result<T, Error>
835 where
836 MK: Fn() -> Box<dyn FnOnce(&mut rusqlite::Connection) -> Result<T, rusqlite::Error> + Send>,
837 T: Send + 'static,
838 {
839 let (pool, generation) = self.current();
840 match pool.conn_mut(make()).await {
841 Err(e) => {
842 self.recover(e, generation)?;
843 self.current().0.conn_mut(make()).await
844 }
845 r => r,
846 }
847 }
848
849 pub fn remove_last_column(&self) -> Result<(), Error> {
854 let this = self.clone();
855 self.conn_blocking(move |conn| {
856 let columns = Self::get_unique_value(conn, this.control_table(), "columns", 0u32)?;
857 if columns == 0 {
858 return Err(rusqlite::Error::QueryReturnedNoRows);
859 }
860 Self::set_unique_value(conn, this.control_table(), "columns", columns - 1)?;
861
862 conn.execute(
863 &format!("DROP TABLE {}", get_column_table_name(columns - 1)),
864 [],
865 )?;
866 Ok(())
867 })
868 }
869
870 pub fn add_column(&self) -> Result<(), Error> {
872 let this = self.clone();
873
874 self.conn_blocking(move |conn| {
875 let columns = Self::get_unique_value(conn, this.control_table(), "columns", 0u32)?;
876 Self::set_unique_value(conn, this.control_table(), "columns", columns + 1)?;
877 Self::create_column_table(conn, columns)
878 })
879 }
880 pub fn transaction(&self) -> DBTransaction {
882 DBTransaction::new()
883 }
884
885 pub async fn vacuum(&self) -> Result<(), Error> {
887 let vacuum_mode = self.config().vacuum_mode;
888 self.conn_retry(|| {
889 Box::new(move |conn: &rusqlite::Connection| {
890 match vacuum_mode {
891 VacuumMode::None => {}
892 VacuumMode::Incremental => {
893 conn.execute("PRAGMA incremental_vacuum", [])?;
894 }
895 VacuumMode::Full => {
896 conn.execute("VACUUM", [])?;
897 }
898 }
899 conn.pragma_update(None, "wal_checkpoint", "TRUNCATE")?;
900 Ok(())
901 })
902 })
903 .await
904 }
905
906 fn validate_column(&self, col: u32) -> rusqlite::Result<()> {
910 if col >= self.columns() {
911 return Err(rusqlite::Error::InvalidColumnIndex(col as usize));
912 }
913 Ok(())
914 }
915
916 fn create_column_table(conn: &rusqlite::Connection, column: u32) -> rusqlite::Result<()> {
917 conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (id INTEGER PRIMARY KEY AUTOINCREMENT, [key] TEXT UNIQUE, value BLOB)", get_column_table_name(column)), []).map(drop)
918 }
919
920 fn get_unique_value<V>(
921 conn: &rusqlite::Connection,
922 table: Arc<DatabaseTable>,
923 key: &str,
924 default: V,
925 ) -> rusqlite::Result<V>
926 where
927 V: FromStr,
928 {
929 let mut stmt = conn.prepare_cached(&table.str_get_unique_value)?;
930
931 if let Ok(found) = stmt.query_row([key], |row| -> rusqlite::Result<String> { row.get(0) }) {
932 if let Ok(v) = V::from_str(&found) {
933 return Ok(v);
934 }
935 }
936 Ok(default)
937 }
938
939 fn set_unique_value<V>(
940 conn: &rusqlite::Connection,
941 table: Arc<DatabaseTable>,
942 key: &str,
943 value: V,
944 ) -> rusqlite::Result<()>
945 where
946 V: ToString,
947 {
948 let mut stmt = conn.prepare_cached(&table.str_set_unique_value)?;
949
950 let changed = stmt.execute([key, value.to_string().as_str()])?;
951
952 if changed > 1 {
955 return Err(rusqlite::Error::StatementChangedRows(changed));
956 }
957 if changed == 0 {
958 return Err(rusqlite::Error::QueryReturnedNoRows);
959 }
960
961 Ok(())
962 }
963
964 fn has_value(
965 conn: &rusqlite::Connection,
966 table: Arc<DatabaseTable>,
967 key: &str,
968 ) -> rusqlite::Result<bool> {
969 let mut stmt = conn.prepare_cached(&table.str_has_value)?;
970 stmt.exists([key])
971 }
972
973 fn has_value_like(
974 conn: &rusqlite::Connection,
975 table: Arc<DatabaseTable>,
976 key: &str,
977 ) -> rusqlite::Result<bool> {
978 let mut stmt = conn.prepare_cached(&table.str_has_value_like)?;
979 stmt.exists([key])
980 }
981
982 fn load_unique_value_blob(
983 conn: &rusqlite::Connection,
984 table: Arc<DatabaseTable>,
985 key: &str,
986 ) -> rusqlite::Result<Option<Vec<u8>>> {
987 let mut stmt = conn.prepare_cached(&table.str_get_unique_value)?;
988
989 stmt.query_row([key], |row| -> rusqlite::Result<Vec<u8>> { row.get(0) })
990 .optional()
991 }
992
993 fn load_first_value_blob_like(
994 conn: &rusqlite::Connection,
995 table: Arc<DatabaseTable>,
996 like: &str,
997 ) -> rusqlite::Result<Option<(String, Vec<u8>)>> {
998 let mut stmt = conn.prepare_cached(&table.str_get_first_value_like)?;
999
1000 stmt.query_row([like], |row| -> rusqlite::Result<(String, Vec<u8>)> {
1001 Ok((row.get(0)?, row.get(1)?))
1002 })
1003 .optional()
1004 }
1005
1006 fn store_unique_value_blob(
1007 conn: &rusqlite::Connection,
1008 table: Arc<DatabaseTable>,
1009 key: &str,
1010 value: &[u8],
1011 ) -> rusqlite::Result<()> {
1012 let mut stmt = conn.prepare_cached(&table.str_set_unique_value)?;
1013
1014 let changed = stmt.execute(params![key, value])?;
1015 if changed > 1 {
1018 return Err(rusqlite::Error::StatementChangedRows(changed));
1019 }
1020 if changed == 0 {
1021 return Err(rusqlite::Error::QueryReturnedNoRows);
1022 }
1023 Ok(())
1024 }
1025
1026 fn remove_unique_value_blob(
1027 conn: &rusqlite::Connection,
1028 table: Arc<DatabaseTable>,
1029 key: &str,
1030 ) -> rusqlite::Result<()> {
1031 let mut stmt = conn.prepare_cached(&table.str_remove_unique_value)?;
1032
1033 let _ = stmt.execute([key])?;
1034
1035 Ok(())
1036 }
1037
1038 fn remove_and_return_unique_value_blob(
1039 conn: &rusqlite::Connection,
1040 table: Arc<DatabaseTable>,
1041 key: &str,
1042 ) -> rusqlite::Result<Option<Vec<u8>>> {
1043 let mut stmt = conn.prepare_cached(&table.str_remove_and_return_unique_value)?;
1044
1045 stmt.query_row([key], |row| -> rusqlite::Result<Vec<u8>> { row.get(0) })
1046 .optional()
1047 }
1048
1049 fn remove_unique_value_blob_like(
1050 conn: &rusqlite::Connection,
1051 table: Arc<DatabaseTable>,
1052 like: &str,
1053 ) -> rusqlite::Result<usize> {
1054 let mut stmt = conn.prepare_cached(&table.str_remove_unique_value_like)?;
1055
1056 let changed = stmt.execute([like])?;
1057 Ok(changed)
1058 }
1059
1060 fn open_resize_columns(&self) -> io::Result<()> {
1061 let columns = self.columns();
1062 let this = self.clone();
1063 self.conn_blocking(move |conn| {
1064 conn.execute("CREATE TABLE IF NOT EXISTS control (id INTEGER PRIMARY KEY AUTOINCREMENT, [key] TEXT UNIQUE, value TEXT)", [])?;
1066
1067 let on_disk_columns =
1069 Self::get_unique_value(conn, this.control_table(), "columns", 0u32)?;
1070
1071 if columns <= on_disk_columns {
1073 return Ok(());
1074 }
1075
1076 for cn in on_disk_columns..columns {
1078 Self::create_column_table(conn, cn)?;
1080 }
1081 Self::set_unique_value(
1082 conn,
1083 this.control_table(),
1084 "columns",
1085 columns,
1086 )?;
1087 Ok(())
1088 }).map_err(io::Error::other)
1089 }
1090
1091 fn stats_read(&self, count: usize, bytes: usize) {
1092 let mut inner = self.inner.lock();
1093 inner.current_stats.reads += count as u64;
1094 inner.overall_stats.reads += count as u64;
1095 inner.current_stats.bytes_read += bytes as u64;
1096 inner.overall_stats.bytes_read += bytes as u64;
1097 }
1098
1099 fn stats_write(&self, sizes: &[usize]) {
1100 if sizes.is_empty() {
1101 return;
1102 }
1103
1104 let mut inner = self.inner.lock();
1105 for &size in sizes {
1106 inner.current_stats.record_write(size);
1107 inner.overall_stats.record_write(size);
1108 }
1109 }
1110
1111 fn stats_tx_write(&self, size: usize, duration: Duration) {
1112 let mut inner = self.inner.lock();
1113 inner
1114 .current_stats
1115 .record_tx_write(size, duration.as_micros() as f64);
1116 inner
1117 .overall_stats
1118 .record_tx_write(size, duration.as_micros() as f64);
1119 }
1120
1121 fn stats_delete(&self, count: usize) {
1122 if count == 0 {
1123 return;
1124 }
1125
1126 let mut inner = self.inner.lock();
1127 inner.current_stats.deletes += count as u64;
1128 inner.overall_stats.deletes += count as u64;
1129 }
1130
1131 fn stats_delete_prefix(&self, count: usize) {
1132 if count == 0 {
1133 return;
1134 }
1135
1136 let mut inner = self.inner.lock();
1137 inner.current_stats.prefix_deletes += count as u64;
1138 inner.overall_stats.prefix_deletes += count as u64;
1139 }
1140
1141 fn stats_transaction(&self, count: usize) {
1142 let mut inner = self.inner.lock();
1143 inner.current_stats.transactions += count as u64;
1144 inner.overall_stats.transactions += count as u64;
1145 }
1146}
1147
1148impl KeyValueDB for Database {
1149 fn get(&self, col: u32, key: &[u8]) -> KeyValueDBPinBoxFuture<'_, io::Result<Option<DBValue>>> {
1150 let key_text = key_to_text(key);
1151 let key_len = key.len();
1152
1153 Box::pin(async move {
1154 self.validate_column(col).map_err(io::Error::other)?;
1155 let someval = self
1156 .conn_retry_blocking(|| {
1157 let that = self.clone();
1158 let key_text = key_text.clone();
1159 Box::new(move |conn: &rusqlite::Connection| {
1160 Self::load_unique_value_blob(conn, that.column_table(col), &key_text)
1161 })
1162 })
1163 .map_err(io::Error::other)?;
1164 {
1165 match &someval {
1166 Some(val) => self.stats_read(1, key_len + val.len()),
1167 None => self.stats_read(1, key_len),
1168 }
1169 }
1170
1171 Ok(someval)
1172 })
1173 }
1174
1175 fn delete(
1177 &self,
1178 col: u32,
1179 key: &[u8],
1180 ) -> KeyValueDBPinBoxFuture<'_, io::Result<Option<DBValue>>> {
1181 let key_text = key_to_text(key);
1182 let key_len = key.len();
1183
1184 Box::pin(async move {
1185 self.validate_column(col).map_err(io::Error::other)?;
1186 self.conn_retry_blocking(|| {
1187 let that = self.clone();
1188 let key_text = key_text.clone();
1189 Box::new(move |conn: &rusqlite::Connection| {
1190 let someval = Self::remove_and_return_unique_value_blob(
1191 conn,
1192 that.column_table(col),
1193 &key_text,
1194 )?;
1195
1196 match &someval {
1197 Some(val) => {
1198 that.stats_read(1, key_len + val.len());
1199 }
1200 None => that.stats_read(1, key_len),
1201 }
1202
1203 Ok(someval)
1204 })
1205 })
1206 .map_err(io::Error::other)
1207 })
1208 }
1209
1210 fn write(
1211 &self,
1212 transaction: DBTransaction,
1213 ) -> KeyValueDBPinBoxFuture<'_, Result<(), DBTransactionError>> {
1214 let transaction = Arc::new(transaction);
1215 Box::pin(async move {
1216 self.stats_transaction(1);
1217
1218 self.conn_mut_retry(|| {
1219 let that = self.clone();
1220 let transaction_clone = transaction.clone();
1221 Box::new(move |conn: &mut rusqlite::Connection| {
1222 let mut sizes = Vec::with_capacity(transaction_clone.ops.len());
1223 let mut total_tx_size = 0;
1224 let mut deletes = 0usize;
1225 let mut prefix_deletes = 0usize;
1226 let start = Instant::now();
1227
1228 let tx = conn.transaction()?;
1229
1230 for op in &transaction_clone.ops {
1231 match op {
1232 DBOp::Insert { col, key, value } => {
1233 that.validate_column(*col)?;
1234 Self::store_unique_value_blob(
1235 &tx,
1236 that.column_table(*col),
1237 &key_to_text(key),
1238 value,
1239 )?;
1240 sizes.push(key.len() + value.len());
1241 total_tx_size += key.len() + value.len();
1242 }
1243 DBOp::Delete { col, key } => {
1244 that.validate_column(*col)?;
1245 Self::remove_unique_value_blob(
1246 &tx,
1247 that.column_table(*col),
1248 &key_to_text(key),
1249 )?;
1250 deletes += 1;
1251 }
1252 DBOp::DeletePrefix { col, prefix } => {
1253 that.validate_column(*col)?;
1254 Self::remove_unique_value_blob_like(
1255 &tx,
1256 that.column_table(*col),
1257 &(like_key_to_text(prefix) + "%"),
1258 )?;
1259 prefix_deletes += 1;
1260 }
1261 }
1262 }
1263 tx.commit()?;
1264
1265 let duration = Instant::now() - start;
1266 that.stats_write(&sizes);
1267 that.stats_tx_write(total_tx_size, duration);
1268 that.stats_delete(deletes);
1269 that.stats_delete_prefix(prefix_deletes);
1270
1271 Ok(())
1272 })
1273 })
1274 .await
1275 .map_err(io::Error::other)
1276 .map_err(|error| {
1277 let transaction = transaction.as_ref().clone();
1278 DBTransactionError { error, transaction }
1279 })
1280 })
1281 }
1282
1283 fn iter<
1284 'a,
1285 T: Send + 'static,
1286 C: Send + 'static,
1287 F: FnMut(&mut C, DBKeyValueRef) -> io::Result<Option<T>> + Send + Sync + 'static,
1288 >(
1289 &'a self,
1290 col: u32,
1291 prefix: Option<&'a [u8]>,
1292 context: C,
1293 f: F,
1294 ) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>> {
1295 let opt_prefix_query = prefix.map(|p| like_key_to_text(p) + "%");
1296 Box::pin(async move {
1297 if col >= self.columns() {
1298 return Err(io::Error::from(io::ErrorKind::NotFound));
1299 }
1300
1301 let context = Arc::new(Mutex::new(Some(context)));
1302 let f = Arc::new(Mutex::new(f));
1303 let called = Arc::new(AtomicBool::new(false));
1304
1305 let make = || {
1306 let that = self.clone();
1307 let context_ref = context.clone();
1308 let f_ref = f.clone();
1309 let called = called.clone();
1310 let opt_prefix_query = opt_prefix_query.clone();
1311 Box::new(move |conn: &rusqlite::Connection| {
1312 let mut context = context_ref.lock();
1313 let context = context.as_mut().unwrap();
1314 let mut f_guard = f_ref.lock();
1315 let f = &mut *f_guard;
1316
1317 let mut stmt;
1318 let mut rows;
1319 if let Some(prefix_query) = opt_prefix_query {
1320 stmt = conn.prepare_cached(&that.column_table(col).str_iter_with_prefix)?;
1321 rows = stmt.query([prefix_query])?;
1322 } else {
1323 stmt = conn.prepare_cached(&that.column_table(col).str_iter_no_prefix)?;
1324 rows = stmt.query([])?;
1325 }
1326
1327 let mut sw = 0usize;
1328 let mut sbw = 0usize;
1329
1330 let out = loop {
1331 match rows.next()? {
1332 Some(row) => {
1334 let kt: String = row.get(0)?;
1335 let v: Vec<u8> = row.get(1)?;
1336 let k: Vec<u8> = match text_to_key(&kt) {
1337 Err(e) => {
1338 break Err(io::Error::other(format!(
1339 "SQLite row get column 0 text convert error: {:?}",
1340 e
1341 )));
1342 }
1343 Ok(v) => v,
1344 };
1345
1346 sw += 1;
1347 sbw += k.len() + v.len();
1348
1349 called.store(true, Ordering::Relaxed);
1350 match f(context, (&k, &v)) {
1351 Ok(None) => (),
1352 Ok(Some(out)) => break Ok(Some(out)),
1354 Err(e) => break Err(e),
1356 }
1357 }
1358 None => {
1360 break Ok(None);
1361 }
1362 }
1363 };
1364
1365 that.stats_read(sw, sbw);
1366
1367 Ok(out)
1368 })
1369 };
1370
1371 let (pool, generation) = self.current();
1372 let res = match pool.conn(make()).await {
1373 Err(e) if !called.load(Ordering::Relaxed) => match self.recover(e, generation) {
1374 Ok(()) => self.current().0.conn(make()).await,
1375 Err(e) => Err(e),
1376 },
1377 Err(e) => {
1378 if self.repair_enabled() && error_is_corruption(&e) {
1381 let _ = self.repair_live(e.to_string(), generation);
1382 }
1383 Err(e)
1384 }
1385 r => r,
1386 };
1387 let res = res.map_err(io::Error::other)?;
1388
1389 let context = context.lock().take().unwrap();
1390
1391 res.map(|x| (context, x))
1392 })
1393 }
1394
1395 fn iter_keys<
1396 'a,
1397 T: Send + 'static,
1398 C: Send + 'static,
1399 F: FnMut(&mut C, DBKeyRef) -> io::Result<Option<T>> + Send + Sync + 'static,
1400 >(
1401 &'a self,
1402 col: u32,
1403 prefix: Option<&'a [u8]>,
1404 context: C,
1405 f: F,
1406 ) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>> {
1407 let opt_prefix_query = prefix.map(|p| like_key_to_text(p) + "%");
1408 Box::pin(async move {
1409 if col >= self.columns() {
1410 return Err(io::Error::from(io::ErrorKind::NotFound));
1411 }
1412
1413 let context = Arc::new(Mutex::new(Some(context)));
1414 let f = Arc::new(Mutex::new(f));
1415 let called = Arc::new(AtomicBool::new(false));
1416
1417 let make = || {
1418 let that = self.clone();
1419 let context_ref = context.clone();
1420 let f_ref = f.clone();
1421 let called = called.clone();
1422 let opt_prefix_query = opt_prefix_query.clone();
1423 Box::new(move |conn: &rusqlite::Connection| {
1424 let mut context = context_ref.lock();
1425 let context = context.as_mut().unwrap();
1426 let mut f_guard = f_ref.lock();
1427 let f = &mut *f_guard;
1428
1429 let mut stmt;
1430 let mut rows;
1431 if let Some(prefix_query) = opt_prefix_query {
1432 stmt =
1433 conn.prepare_cached(&that.column_table(col).str_iter_keys_with_prefix)?;
1434 rows = stmt.query([prefix_query])?;
1435 } else {
1436 stmt =
1437 conn.prepare_cached(&that.column_table(col).str_iter_keys_no_prefix)?;
1438 rows = stmt.query([])?;
1439 }
1440
1441 let mut sw = 0usize;
1442 let mut sbw = 0usize;
1443
1444 let out = loop {
1445 match rows.next()? {
1446 Some(row) => {
1448 let kt: String = row.get(0)?;
1449 let k: Vec<u8> = match text_to_key(&kt) {
1450 Err(e) => {
1451 break Err(io::Error::other(format!(
1452 "SQLite row get column 0 text convert error: {:?}",
1453 e
1454 )));
1455 }
1456 Ok(v) => v,
1457 };
1458
1459 sw += 1;
1460 sbw += k.len();
1461
1462 called.store(true, Ordering::Relaxed);
1463 match f(context, &k) {
1464 Ok(None) => (),
1465 Ok(Some(out)) => break Ok(Some(out)),
1467 Err(e) => break Err(e),
1469 }
1470 }
1471 None => {
1473 break Ok(None);
1474 }
1475 }
1476 };
1477
1478 that.stats_read(sw, sbw);
1479
1480 Ok(out)
1481 })
1482 };
1483
1484 let (pool, generation) = self.current();
1485 let res = match pool.conn(make()).await {
1486 Err(e) if !called.load(Ordering::Relaxed) => match self.recover(e, generation) {
1487 Ok(()) => self.current().0.conn(make()).await,
1488 Err(e) => Err(e),
1489 },
1490 Err(e) => {
1491 if self.repair_enabled() && error_is_corruption(&e) {
1494 let _ = self.repair_live(e.to_string(), generation);
1495 }
1496 Err(e)
1497 }
1498 r => r,
1499 };
1500 let res = res.map_err(io::Error::other)?;
1501
1502 let context = context.lock().take().unwrap();
1503
1504 res.map(|x| (context, x))
1505 })
1506 }
1507
1508 fn io_stats(&self, kind: IoStatsKind) -> IoStats {
1509 fn duration_since(timestamp_microseconds: u64) -> Duration {
1510 std::time::SystemTime::now()
1511 .duration_since(std::time::UNIX_EPOCH)
1512 .map_or(Duration::from_micros(0), |time| {
1513 let now = time.as_micros() as u64;
1514 if now >= timestamp_microseconds {
1515 Duration::from_micros(now - timestamp_microseconds)
1516 } else {
1517 Duration::from_micros(0)
1518 }
1519 })
1520 }
1521
1522 let mut inner = self.inner.lock();
1523 match kind {
1524 IoStatsKind::Overall => {
1525 let mut stats = inner.overall_stats.clone();
1526 stats.span = duration_since(stats.started);
1527 stats
1528 }
1529 IoStatsKind::SincePrevious => {
1530 let mut stats = inner.current_stats.clone();
1531 stats.span = duration_since(stats.started);
1532 inner.current_stats = IoStats::empty();
1533 stats
1534 }
1535 }
1536 }
1537
1538 fn num_columns(&self) -> io::Result<u32> {
1539 self.conn_retry_blocking(|| {
1540 let this = self.clone();
1541 Box::new(move |conn: &rusqlite::Connection| {
1542 Self::get_unique_value(conn, this.control_table(), "columns", 0u32)
1543 })
1544 })
1545 .map_err(io::Error::other)
1546 }
1547
1548 fn num_keys(&self, col: u32) -> KeyValueDBPinBoxFuture<'_, io::Result<u64>> {
1549 Box::pin(async move {
1550 self.conn_retry(|| {
1551 Box::new(move |conn: &rusqlite::Connection| {
1552 conn.query_row(
1553 &format!("SELECT Count(*) FROM {}", get_column_table_name(col)),
1554 [],
1555 |row| -> rusqlite::Result<u64> { row.get(0) },
1556 )
1557 })
1558 })
1559 .await
1560 .map_err(|_| io::Error::from(io::ErrorKind::NotFound))
1561 })
1562 }
1563
1564 fn has_key<'a>(
1566 &'a self,
1567 col: u32,
1568 key: &'a [u8],
1569 ) -> KeyValueDBPinBoxFuture<'a, io::Result<bool>> {
1570 let key_text = key_to_text(key);
1571 let key_len = key.len();
1572
1573 Box::pin(async move {
1574 self.validate_column(col).map_err(io::Error::other)?;
1575 let someval = self
1576 .conn_retry_blocking(|| {
1577 let that = self.clone();
1578 let key_text = key_text.clone();
1579 Box::new(move |conn: &rusqlite::Connection| {
1580 Self::has_value(conn, that.column_table(col), &key_text)
1581 })
1582 })
1583 .map_err(io::Error::other)?;
1584
1585 self.stats_read(1, key_len);
1586
1587 Ok(someval)
1588 })
1589 }
1590
1591 fn has_prefix<'a>(
1593 &'a self,
1594 col: u32,
1595 prefix: &'a [u8],
1596 ) -> KeyValueDBPinBoxFuture<'a, io::Result<bool>> {
1597 let prefix_len = prefix.len();
1598 let prefix_text = like_key_to_text(prefix) + "%";
1599
1600 Box::pin(async move {
1601 self.validate_column(col).map_err(io::Error::other)?;
1602 let someval = self
1603 .conn_retry_blocking(|| {
1604 let that = self.clone();
1605 let prefix_text = prefix_text.clone();
1606 Box::new(move |conn: &rusqlite::Connection| {
1607 Self::has_value_like(conn, that.column_table(col), &prefix_text)
1608 })
1609 })
1610 .map_err(io::Error::other)?;
1611
1612 self.stats_read(1, prefix_len);
1613
1614 Ok(someval)
1615 })
1616 }
1617
1618 fn first_with_prefix<'a>(
1620 &'a self,
1621 col: u32,
1622 prefix: &'a [u8],
1623 ) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBKeyValue>>> {
1624 let prefix_len = prefix.len();
1625 let like = like_key_to_text(prefix) + "%";
1626
1627 Box::pin(async move {
1628 self.validate_column(col).map_err(io::Error::other)?;
1629 let someval = self
1630 .conn_retry_blocking(|| {
1631 let that = self.clone();
1632 let like = like.clone();
1633 Box::new(move |conn: &rusqlite::Connection| {
1634 Self::load_first_value_blob_like(conn, that.column_table(col), &like)
1635 })
1636 })
1637 .map_err(io::Error::other)?;
1638
1639 self.stats_read(1, prefix_len);
1640
1641 match someval {
1642 Some((kt, val)) => match text_to_key(&kt) {
1643 Err(e) => Err(io::Error::other(format!(
1644 "SQLite row get column 0 text convert error: {:?}",
1645 e
1646 ))),
1647 Ok(k) => Ok(Some((k, val))),
1648 },
1649 None => Ok(None),
1650 }
1651 })
1652 }
1653
1654 fn cleanup(&self) -> KeyValueDBPinBoxFuture<'_, io::Result<()>> {
1656 Box::pin(async { self.vacuum().await.map_err(io::Error::other) })
1657 }
1658}
1659
1660#[cfg(test)]
1661mod tests {
1662
1663 use super::*;
1664 use keyvaluedb_shared_tests as st;
1665 use tempfile::Builder as TempfileBuilder;
1666
1667 fn create(columns: u32) -> io::Result<Database> {
1668 let tempfile = TempfileBuilder::new()
1669 .prefix("")
1670 .tempfile()?
1671 .path()
1672 .to_path_buf();
1673 let config = DatabaseConfig::new().with_columns(columns);
1674 Database::open(tempfile, config)
1675 }
1676
1677 fn create_vacuum_mode(columns: u32, vacuum_mode: VacuumMode) -> io::Result<Database> {
1678 let tempfile = TempfileBuilder::new()
1679 .prefix("")
1680 .tempfile()?
1681 .path()
1682 .to_path_buf();
1683 let config = DatabaseConfig::new()
1684 .with_columns(columns)
1685 .with_vacuum_mode(vacuum_mode);
1686 Database::open(tempfile, config)
1687 }
1688
1689 #[tokio::test]
1690 async fn get_fails_with_non_existing_column() -> io::Result<()> {
1691 let db = create(1)?;
1692 st::test_get_fails_with_non_existing_column(db).await
1693 }
1694
1695 #[tokio::test]
1696 async fn num_keys() -> io::Result<()> {
1697 let db = create(1)?;
1698 st::test_num_keys(db).await
1699 }
1700
1701 #[tokio::test]
1702 async fn put_and_get() -> io::Result<()> {
1703 let db = create(1)?;
1704 st::test_put_and_get(db).await
1705 }
1706
1707 #[tokio::test]
1708 async fn delete_and_get() -> io::Result<()> {
1709 let db = create(1)?;
1710 st::test_delete_and_get(db).await
1711 }
1712
1713 #[tokio::test]
1714 async fn delete_and_get_single() -> io::Result<()> {
1715 let db = create(1)?;
1716 st::test_delete_and_get_single(db).await
1717 }
1718
1719 #[tokio::test]
1720 async fn delete_prefix() -> io::Result<()> {
1721 let db = create(st::DELETE_PREFIX_NUM_COLUMNS)?;
1722 st::test_delete_prefix(db).await
1723 }
1724
1725 #[tokio::test]
1726 async fn iter() -> io::Result<()> {
1727 let db = create(1)?;
1728 st::test_iter(db).await
1729 }
1730
1731 #[tokio::test]
1732 async fn iter_keys() -> io::Result<()> {
1733 let db = create(1)?;
1734 st::test_iter_keys(db).await
1735 }
1736
1737 #[tokio::test]
1738 async fn iter_with_prefix() -> io::Result<()> {
1739 let db = create(1)?;
1740 st::test_iter_with_prefix(db).await
1741 }
1742
1743 #[tokio::test]
1744 async fn complex() -> io::Result<()> {
1745 let db = create(1)?;
1746 st::test_complex(db).await
1747 }
1748
1749 #[tokio::test]
1750 async fn cleanup() -> io::Result<()> {
1751 let db = create(1)?;
1752 st::test_cleanup(db).await?;
1753
1754 let db = create_vacuum_mode(1, VacuumMode::None)?;
1755 st::test_cleanup(db).await?;
1756
1757 let db = create_vacuum_mode(1, VacuumMode::Incremental)?;
1758 st::test_cleanup(db).await?;
1759
1760 let db = create_vacuum_mode(1, VacuumMode::Full)?;
1761 st::test_cleanup(db).await?;
1762
1763 let tempfile = TempfileBuilder::new()
1764 .prefix("")
1765 .tempfile()?
1766 .path()
1767 .to_path_buf();
1768 let config = DatabaseConfig::new().with_vacuum_mode(VacuumMode::None);
1769 let db = Database::open(tempfile.clone(), config)?;
1770 st::test_cleanup(db).await?;
1771
1772 let config = DatabaseConfig::new().with_vacuum_mode(VacuumMode::Incremental);
1773 let db = Database::open(tempfile.clone(), config)?;
1774 st::test_cleanup(db).await?;
1775
1776 let config = DatabaseConfig::new().with_vacuum_mode(VacuumMode::Full);
1777 let db = Database::open(tempfile.clone(), config)?;
1778 st::test_cleanup(db).await?;
1779
1780 let config = DatabaseConfig::new().with_vacuum_mode(VacuumMode::None);
1781 let db = Database::open(tempfile, config)?;
1782 st::test_cleanup(db).await?;
1783
1784 Ok(())
1785 }
1786
1787 #[tokio::test]
1788 async fn stats() -> io::Result<()> {
1789 let db = create(st::IO_STATS_NUM_COLUMNS)?;
1790 st::test_io_stats(db).await
1791 }
1792
1793 #[tokio::test]
1794 #[should_panic]
1795 async fn db_config_with_zero_columns() {
1796 let _cfg = DatabaseConfig::new().with_columns(0);
1797 }
1798
1799 #[tokio::test]
1800 #[should_panic]
1801 async fn open_db_with_zero_columns() {
1802 let cfg = DatabaseConfig::new().with_columns(0);
1803 let _db = Database::open("", cfg);
1804 }
1805
1806 #[tokio::test]
1807 async fn add_columns() {
1808 let config_1 = DatabaseConfig::default();
1809 let config_5 = DatabaseConfig::new().with_columns(5);
1810
1811 let tempfile = TempfileBuilder::new()
1812 .prefix("")
1813 .tempfile()
1814 .unwrap()
1815 .path()
1816 .to_path_buf();
1817
1818 {
1820 let db = Database::open(&tempfile, config_1).unwrap();
1821 assert_eq!(db.num_columns().unwrap(), 1);
1822
1823 for i in 2..=5 {
1824 db.add_column().unwrap();
1825 assert_eq!(db.num_columns().unwrap(), i);
1826 }
1827 }
1828
1829 {
1831 let db = Database::open(&tempfile, config_5).unwrap();
1832 assert_eq!(db.num_columns().unwrap(), 5);
1833 }
1834 }
1835
1836 #[tokio::test]
1837 async fn remove_columns() {
1838 let config_1 = DatabaseConfig::default();
1839 let config_5 = DatabaseConfig::new().with_columns(5);
1840
1841 let tempfile = TempfileBuilder::new()
1842 .prefix("drop_columns")
1843 .tempfile()
1844 .unwrap()
1845 .path()
1846 .to_path_buf();
1847
1848 {
1850 let db = Database::open(&tempfile, config_5).expect("open with 5 columns");
1851 assert_eq!(db.num_columns().unwrap(), 5);
1852
1853 for i in (1..5).rev() {
1854 db.remove_last_column().unwrap();
1855 assert_eq!(db.num_columns().unwrap(), i);
1856 }
1857 }
1858
1859 {
1861 let db = Database::open(&tempfile, config_1).unwrap();
1862 assert_eq!(db.num_columns().unwrap(), 1);
1863 }
1864 }
1865
1866 #[tokio::test]
1867 async fn test_num_keys() {
1868 let tempfile = TempfileBuilder::new()
1869 .prefix("")
1870 .tempfile()
1871 .unwrap()
1872 .path()
1873 .to_path_buf();
1874 let config = DatabaseConfig::new().with_columns(1);
1875 let db = Database::open(tempfile, config).unwrap();
1876
1877 assert_eq!(
1878 db.num_keys(0).await.unwrap(),
1879 0,
1880 "database is empty after creation"
1881 );
1882 let key1 = b"beef";
1883 let mut batch = db.transaction();
1884 batch.put(0, key1, key1);
1885 db.write(batch).await.unwrap();
1886 assert_eq!(
1887 db.num_keys(0).await.unwrap(),
1888 1,
1889 "adding a key increases the count"
1890 );
1891 }
1892}