storage/shared/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::error::Error as StdError;
6
7use libc::ENOSPC;
8use rusqlite::{Error as RusqliteError, ffi};
9
10// These pragmas need to be set once
11pub const DB_INIT_PRAGMAS: [&str; 2] =
12    ["PRAGMA journal_mode = WAL;", "PRAGMA encoding = 'UTF-16';"];
13
14// These pragmas need to be set once for in memory databases
15pub const DB_IN_MEMORY_INIT_PRAGMAS: [&str; 1] = ["PRAGMA encoding = 'UTF-16';"];
16
17// These pragmas need to be run once per connection.
18pub const DB_PRAGMAS: [&str; 4] = [
19    "PRAGMA synchronous = NORMAL;",
20    "PRAGMA journal_size_limit = 67108864 -- 64 megabytes;",
21    "PRAGMA mmap_size = 67108864 -- 64 megabytes;",
22    "PRAGMA cache_size = 2000;",
23];
24
25// These pragmas need to be run once per connection for in memory databases.
26pub const DB_IN_MEMORY_PRAGMAS: [&str; 1] = ["PRAGMA cache_size = 2000;"];
27
28pub(crate) fn is_sqlite_disk_full_error(error: &RusqliteError) -> bool {
29    fn has_enospc(mut source: Option<&(dyn StdError + 'static)>) -> bool {
30        while let Some(err) = source {
31            if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
32                if io_err.raw_os_error() == Some(ENOSPC) {
33                    return true;
34                }
35            }
36            source = err.source();
37        }
38        false
39    }
40
41    // Walk the full chain (including `error` itself).
42    let saw_enospc = has_enospc(Some(error as &(dyn StdError + 'static)));
43
44    match error {
45        RusqliteError::SqliteFailure(sqlite_err, _) => {
46            // High confidence "database or disk is full".
47            if sqlite_err.code == ffi::ErrorCode::DiskFull ||
48                sqlite_err.extended_code == ffi::SQLITE_FULL
49            {
50                return true;
51            }
52
53            // Only treat IO errors as quota-related if ENOSPC is present.
54            if saw_enospc &&
55                matches!(
56                    sqlite_err.extended_code,
57                    ffi::SQLITE_IOERR |
58                        ffi::SQLITE_IOERR_WRITE |
59                        ffi::SQLITE_IOERR_FSYNC |
60                        ffi::SQLITE_IOERR_DIR_FSYNC |
61                        ffi::SQLITE_IOERR_TRUNCATE |
62                        ffi::SQLITE_IOERR_MMAP
63                )
64            {
65                return true;
66            }
67
68            false
69        },
70        _ => saw_enospc,
71    }
72}