Skip to main content

keyvaluedb_sqlite/
lib.rs

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///////////////////////////////////////////////////////////////////////////////
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq)]
26pub enum VacuumMode {
27    None,
28    Incremental,
29    Full,
30}
31
32/// Which integrity check gates the open-time repair
33#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34pub enum RepairCheck {
35    /// No open-time check; repair still runs when the file will not open or
36    /// an operation hits corruption at runtime
37    None,
38    /// PRAGMA quick_check: page structure only, misses index-vs-table
39    /// disagreement
40    Quick,
41    /// PRAGMA integrity_check: the full verdict, catches everything sqlite
42    /// can detect
43    Full,
44}
45
46/// Called with a report every time a repair rebuilds the database
47pub type RepairCallback = Arc<dyn Fn(&RepairReport) + Send + Sync>;
48
49/// Database configuration
50#[derive(Clone)]
51pub struct DatabaseConfig {
52    /// Set number of columns.
53    /// The number of columns must not be zero.
54    pub columns: u32,
55    /// Set flags used to open the database
56    pub flags: OpenFlags,
57    /// Number of connections to open
58    pub num_conns: usize,
59    /// Vacuum mode
60    pub vacuum_mode: VacuumMode,
61    /// On corruption, at open or at runtime, salvage the readable rows into a
62    /// fresh database, keeping the damaged files beside it
63    pub repair_on_corrupt: bool,
64    /// Which integrity check gates the open-time repair
65    pub repair_check: RepairCheck,
66    /// Skip the open-time check when the database (plus WAL) is larger than
67    /// this; the check reads every page, so on a large store it can hold up
68    /// open for a long time, and runtime repair still covers what it would
69    /// have caught. None checks regardless of size.
70    pub repair_check_byte_limit: Option<u64>,
71    /// Called with a report every time a repair runs
72    pub on_repair: Option<RepairCallback>,
73}
74
75impl DatabaseConfig {
76    /// Create new `DatabaseConfig` with default parameters
77    pub fn new() -> Self {
78        Default::default()
79    }
80
81    /// Set the number of columns. `columns` must not be zero.
82    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    /// Enable corruption detection and salvage, at open and at runtime
88    pub fn with_repair_on_corrupt(self, repair_on_corrupt: bool) -> Self {
89        Self {
90            repair_on_corrupt,
91            ..self
92        }
93    }
94
95    /// Set which integrity check gates the open-time repair
96    pub fn with_repair_check(self, repair_check: RepairCheck) -> Self {
97        Self {
98            repair_check,
99            ..self
100        }
101    }
102
103    /// Skip the open-time check for databases larger than `bytes`
104    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    /// Set a callback invoked with a report every time a repair runs
112    pub fn with_on_repair(self, on_repair: RepairCallback) -> Self {
113        Self {
114            on_repair: Some(on_repair),
115            ..self
116        }
117    }
118
119    /// Sets the flags to 'in-memory database'
120    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    /// Replaces all the flags
131    pub fn with_flags(self, flags: OpenFlags) -> Self {
132        Self { flags, ..self }
133    }
134
135    /// Sets the number of connections for this database
136    pub fn with_num_conns(self, num_conns: usize) -> Self {
137        Self { num_conns, ..self }
138    }
139
140    /// Set the vacuum mode used by 'cleanup'
141    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
166///////////////////////////////////////////////////////////////////////////////
167
168/// Whether a rusqlite error says the file is damaged
169fn 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
179/// Whether a pool error says the file is damaged
180fn error_is_corruption(e: &Error) -> bool {
181    matches!(e, Error::Rusqlite(e) if code_is_corruption(e))
182}
183
184/// Whether an error chain bottoms out in sqlite saying the file is damaged
185fn 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
199///////////////////////////////////////////////////////////////////////////////
200
201/// An sqlite table with its statement strings
202pub 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///////////////////////////////////////////////////////////////////////////////
267
268/// What the automatic corruption repair did, kept on the database it produced
269#[derive(Debug, Clone)]
270pub struct RepairReport {
271    /// The integrity failure that triggered the repair
272    pub detected: String,
273    /// Rows salvaged across the control table and all column tables
274    pub rows_recovered: u64,
275    /// Tables whose salvage scan ended early on a damaged page
276    pub partial_tables: u32,
277    /// Where the damaged database files were moved
278    pub corrupt_path: PathBuf,
279}
280
281/// The swappable engine state: a repair closes the pool, swaps the files and
282/// installs a fresh pool, bumping the generation so racing operations know to
283/// retry rather than fail
284struct DatabaseState {
285    pool: Pool,
286    generation: u64,
287    repair_report: Option<RepairReport>,
288}
289
290/// What a salvage scan pulled out of a damaged database
291#[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
306/// The database's on-disk size: main file plus WAL
307fn 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
312/// Move the damaged files aside as `<name>.corrupt` and the rebuilt
313/// `.repairing` files into place, returning where the damaged files went
314fn 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
327/// An sqlite key-value database fulfilling the `KeyValueDB` trait
328pub 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    ////////////////////////////////////////////////////////////////
355    // Initialization
356
357    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            // The open-time check reads every page; above the byte limit skip
368            // it and let runtime repair cover what it would have caught
369            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        // Fold the WAL only after the integrity verdict; a damaged WAL must be
386        // quarantined by the repair, never folded into the main file
387        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    /// Open the connection pool and apply the session pragmas to every
399    /// connection in it
400    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            // Don't rely on STATEMENT_CACHE_DEFAULT_CAPACITY in rusqlite, set it explicitly
413            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            // Wait out cross-process lock contention instead of failing immediately
419            conn.pragma_update(None, "busy_timeout", 2000)?;
420            // Catch page scribbles at the operation that hits them, where the
421            // runtime repair can act, instead of letting them spread
422            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    /// What the most recent repair did, if any has run
502    pub fn repair_report(&self) -> Option<RepairReport> {
503        self.unlocked_inner.state.read().repair_report.clone()
504    }
505
506    /// The current pool and its generation; the generation changes when a
507    /// repair swaps the pool out
508    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    /// Integrity check on one connection. None = clean; Some = what is wrong.
518    /// A check that cannot even run to completion errs with the corruption it hit.
519    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    /// Rebuild a damaged database from whatever rows are still readable.
543    ///
544    /// The damaged files move beside the store as `<name>.corrupt` rather than
545    /// being destroyed: corruption that sqlite's own crash-safety should have
546    /// made impossible is evidence worth keeping. `old` is the still-open
547    /// handle when the damage was found by the integrity check; None when the
548    /// file would not even open, in which case nothing is salvageable and the
549    /// result is a fresh empty database.
550    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        // Close the old handle so every file is settled before the swap
568        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    /// Repair in place after an operation hit corruption at runtime.
584    ///
585    /// Holds the state write lock throughout: new operations wait, operations
586    /// already queued drain when the old pool closes, and the generation bump
587    /// tells racing callers that failed meanwhile to retry. Ok means the
588    /// database was repaired (or a racing caller already had); Err means the
589    /// repair could not run and the original operation error stands.
590    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            // Keep serving from the damaged file rather than die closed
604            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    /// Read every row a damaged database will still yield, all tables through
630    /// one connection under an exclusive transaction: repair must not scan, or
631    /// swap files afterward, while any other writer, in this process or
632    /// another, is mid-write. Errs busy if another writer holds the database.
633    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            // Salvage every table the database says it has, or at least the
638            // configured set if its control table cannot say
639            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    /// Build the replacement database at `<db>.repairing` from the salvaged
667    /// rows, returning how many were restored. The replacement is closed and
668    /// settled on return, ready to swap in.
669    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            // INSERT OR IGNORE: values the fresh open already wrote
687            // (control's own column count) win over salvaged ones
688            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    /// Read every row a damaged table will still yield, stopping at the first
708    /// page it cannot, keeping what came before it
709    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    /// Decide what a failed operation should do: repair and retry, retry
777    /// because a racing repair swapped the pool, or give up with the error.
778    ///
779    /// Reading the generation blocks while a repair holds the write lock, so a
780    /// caller that failed because of a concurrent repair waits it out here and
781    /// then retries against the healthy pool.
782    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        // Not corruption: worth one retry only if a repair swapped the pool
793        // out from under this operation
794        if self.current().1 == seen_generation {
795            return Err(e);
796        }
797        Ok(())
798    }
799
800    /// Run `make()`'s closure on a pool connection, repairing and retrying
801    /// once if it hits corruption (or a concurrent repair)
802    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    /// Blocking variant of `conn_retry`
818    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    /// Mutable-connection variant of `conn_retry`
834    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    ////////////////////////////////////////////////////////////////
850    // Low level operations
851
852    /// Remove the last column family in the database. The deletion is definitive.
853    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    /// Add a new column family to the DB.
871    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    /// Helper to create new transaction for this database.
881    pub fn transaction(&self) -> DBTransaction {
882        DBTransaction::new()
883    }
884
885    /// Vacuum database
886    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    ////////////////////////////////////////////////////////////////
907    // Internal helpers
908
909    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        // Never panic in a pool closure: a panic mid-transaction leaves the
953        // connection holding a stale open transaction
954        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        // Never panic in a pool closure: a panic mid-transaction leaves the
1016        // connection holding a stale open transaction
1017        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			// First see if we have a control table with the number of columns
1065			conn.execute("CREATE TABLE IF NOT EXISTS control (id INTEGER PRIMARY KEY AUTOINCREMENT, [key] TEXT UNIQUE, value TEXT)", [])?;
1066
1067            // Get column count
1068            let on_disk_columns =
1069                Self::get_unique_value(conn, this.control_table(), "columns", 0u32)?;
1070
1071            // If desired column count is less than or equal to current column count, then allow it, but restrict access to columns
1072            if columns <= on_disk_columns {
1073                return Ok(());
1074            }
1075
1076            // Otherwise resize and add other columns
1077            for cn in on_disk_columns..columns {
1078                // Create the column table if we don't have it
1079                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    /// Remove a value by key, returning the old value
1176    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                            // Iterated value
1333                            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                                    // Callback early termination
1353                                    Ok(Some(out)) => break Ok(Some(out)),
1354                                    // Callback error termination
1355                                    Err(e) => break Err(e),
1356                                }
1357                            }
1358                            // Natural iterator termination
1359                            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                    // Rows already reached the callback: repair for the next
1379                    // caller, but this scan cannot safely restart
1380                    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                            // Iterated value
1447                            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                                    // Callback early termination
1466                                    Ok(Some(out)) => break Ok(Some(out)),
1467                                    // Callback error termination
1468                                    Err(e) => break Err(e),
1469                                }
1470                            }
1471                            // Natural iterator termination
1472                            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                    // Rows already reached the callback: repair for the next
1492                    // caller, but this scan cannot safely restart
1493                    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    /// Check for the existence of a value by key.
1565    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    /// Check for the existence of a value by prefix.
1592    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    /// Get the first value matching the given prefix.
1619    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    /// Vacuum database
1655    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        // open 1, add 4.
1819        {
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        // reopen as 5.
1830        {
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        // open 5, remove 4.
1849        {
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        // reopen as 1.
1860        {
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}