atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: crates/matrix-sdk-sqlite/src/lib.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2022 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for the specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
#![cfg_attr(
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
    not(any(feature = "state-store", feature = "crypto-store", feature = "event-cache")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
    allow(dead_code, unused_imports)
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
mod connection;
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
#[cfg(feature = "crypto-store")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
mod crypto_store;
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
mod error;
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
#[cfg(feature = "event-cache")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
mod event_cache_store;
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
#[cfg(feature = "event-cache")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
mod media_store;
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
#[cfg(feature = "state-store")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
mod state_store;
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
mod utils;
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
    cmp::max,
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
    fmt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    path::{Path, PathBuf},
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
use deadpool::managed::PoolConfig;
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
#[cfg(feature = "crypto-store")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
pub use self::crypto_store::SqliteCryptoStore;
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
pub use self::error::OpenStoreError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
#[cfg(feature = "event-cache")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
pub use self::event_cache_store::SqliteEventCacheStore;
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
#[cfg(feature = "event-cache")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
pub use self::media_store::SqliteMediaStore;
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
#[cfg(feature = "state-store")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
pub use self::state_store::{DATABASE_NAME as STATE_STORE_DATABASE_NAME, SqliteStateStore};
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
matrix_sdk_test_utils::init_tracing_for_tests!();
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
/// An enum used to store the secret that gives access to a store
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
#[derive(Clone, Debug, PartialEq, Zeroize, ZeroizeOnDrop)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
pub enum Secret {
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    // Cryptographic key used to open the store
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
    Key(Box<[u8; 32]>),
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
    // Passphrase used to open the store
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
    PassPhrase(Zeroizing<String>),
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
/// A configuration structure used for opening a store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
#[derive(Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
pub struct SqliteStoreConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
    /// Path to the database, without the file name.
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    path: PathBuf,
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    /// Secret to open the store, if any
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
    secret: Option<Secret>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
    /// The pool configuration for [`deadpool`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
    pool_config: PoolConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    /// The runtime configuration to apply when opening an SQLite connection.
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
    runtime_config: RuntimeConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
impl fmt::Debug for SqliteStoreConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
        formatter
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
            .debug_struct("SqliteStoreConfig")
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
            .field("path", &self.path)
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
            .field("pool_config", &self.pool_config)
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
            .field("runtime_config", &self.runtime_config)
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
            .finish_non_exhaustive()
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
/// The minimum size of the connections pool.
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
/// We need at least 2 connections: one connection for write operations, and one
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
/// connection for read operations.
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
const POOL_MINIMUM_SIZE: usize = 2;
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
impl SqliteStoreConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
    /// Create a new [`SqliteStoreConfig`] with a path representing the
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
    /// directory containing the store database.
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
    pub fn new<P>(path: P) -> Self
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
        P: AsRef<Path>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
            path: path.as_ref().to_path_buf(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
            pool_config: PoolConfig::new(max(POOL_MINIMUM_SIZE, num_cpus::get_physical() * 4)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
            runtime_config: RuntimeConfig::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
            secret: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
    /// Similar to [`SqliteStoreConfig::new`], but with defaults tailored for a
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
    /// low memory usage environment.
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
    /// The following defaults are set:
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    /// * The `pool_max_size` is set to the number of physical CPU, so one
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    ///   connection per physical thread,
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    /// * The `cache_size` is set to 500Kib,
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
    /// * The `journal_size_limit` is set to 2Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
    pub fn with_low_memory_config<P>(path: P) -> Self
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
        P: AsRef<Path>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
        Self::new(path)
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
            // Maximum one connection per physical thread.
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
            .pool_max_size(num_cpus::get_physical())
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
            // Cache size is 500Kib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
            .cache_size(500_000)
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
            // Journal size limit is 2Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
            .journal_size_limit(2_000_000)
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    /// Override the path.
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    pub fn path<P>(mut self, path: P) -> Self
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    where
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
        P: AsRef<Path>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
        self.path = path.as_ref().to_path_buf();
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    /// Define the passphrase if the store is encoded.
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    pub fn passphrase(mut self, passphrase: Option<&str>) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
        self.secret =
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
            passphrase.map(|passphrase| Secret::PassPhrase(Zeroizing::new(passphrase.to_owned())));
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
    /// Define the key if the store is encoded.
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
    pub fn key(mut self, key: Option<&[u8; 32]>) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
        self.secret = key.map(|key| Secret::Key(Box::new(*key)));
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
    /// Define the maximum pool size for [`deadpool`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
    /// See [`deadpool::managed::PoolConfig::max_size`] to learn more.
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
    pub fn pool_max_size(mut self, max_size: usize) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
        self.pool_config.max_size = max(POOL_MINIMUM_SIZE, max_size);
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    /// Optimize the database.
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
    /// The SQLite documentation recommends to run this regularly and after any
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
    /// schema change. The easiest is to do it consistently when the store is
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
    /// constructed, after eventual migrations.
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
    /// See [`PRAGMA optimize`] to learn more.
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
    /// The default value is `true`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
    /// [`PRAGMA optimize`]: https://www.sqlite.org/pragma.html#pragma_optimize
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
    pub fn optimize(mut self, optimize: bool) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
        self.runtime_config.optimize = optimize;
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    /// Define the maximum size in **bytes** the SQLite cache can use.
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    /// See [`PRAGMA cache_size`] to learn more.
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
    /// The default value is 2Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    /// [`PRAGMA cache_size`]: https://www.sqlite.org/pragma.html#pragma_cache_size
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
    pub fn cache_size(mut self, cache_size: u32) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
        self.runtime_config.cache_size = cache_size;
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
    /// Limit the size of the WAL file, in **bytes**.
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
    /// By default, while the DB connections of the databases are open, [the
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
    /// size of the WAL file can keep increasing][size_wal_file] depending on
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
    /// the size needed for the transactions. A critical case is `VACUUM`
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
    /// which basically writes the content of the DB file to the WAL file
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
    /// before writing it back to the DB file, so we end up taking twice the
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
    /// size of the database.
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
    /// By setting this limit, the WAL file is truncated after its content is
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
    /// written to the database, if it is bigger than the limit.
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
    /// See [`PRAGMA journal_size_limit`] to learn more. The value `limit`
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
    /// corresponds to `N` in `PRAGMA journal_size_limit = N`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
    /// The default value is 10Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
    /// [size_wal_file]: https://www.sqlite.org/wal.html#avoiding_excessively_large_wal_files
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
    /// [`PRAGMA journal_size_limit`]: https://www.sqlite.org/pragma.html#pragma_journal_size_limit
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
    pub fn journal_size_limit(mut self, limit: u32) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
        self.runtime_config.journal_size_limit = limit;
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
        self
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
    /// Returns the pool configuration.
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
    pub(crate) fn pool_config(&self) -> PoolConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
        self.pool_config
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
    /// Returns the runtime configuration.
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
    pub(crate) fn runtime_config(&self) -> RuntimeConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
        self.runtime_config
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    /// Build a pool of active connections to a particular database.
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
    pub fn build_pool_of_connections(
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
        database_name: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
    ) -> Result<connection::Pool, connection::CreatePoolError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
        let path = self.path.join(database_name);
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
        let manager = connection::Manager::new(path);
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
        connection::Pool::builder(manager)
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
            .config(self.pool_config)
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
            .runtime(connection::RUNTIME)
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
            .build()
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
            .map_err(connection::CreatePoolError::Build)
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
/// This type represents values to set at runtime when a database is opened.
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
/// This configuration is applied by
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
/// [`utils::SqliteAsyncConnExt::apply_runtime_config`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
#[derive(Clone, Copy, Debug)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
struct RuntimeConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
    /// If `true`, [`utils::SqliteAsyncConnExt::optimize`] will be called.
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
    optimize: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
    /// Regardless of the value, [`utils::SqliteAsyncConnExt::cache_size`] will
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
    /// always be called with this value.
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
    cache_size: u32,
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
    /// Regardless of the value,
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
    /// [`utils::SqliteAsyncConnExt::journal_size_limit`] will always be called
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
    /// with this value.
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
    journal_size_limit: u32,
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
impl Default for RuntimeConfig {
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
    fn default() -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
            // Optimize is always applied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
            optimize: true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
            // A cache of 2Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
            cache_size: 2_000_000,
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
            // A limit of 10Mib.
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
            journal_size_limit: 10_000_000,
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
mod tests {
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
    use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
        ops::Not,
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
        path::{Path, PathBuf},
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
    use super::{POOL_MINIMUM_SIZE, Secret, SqliteStoreConfig};
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
    fn test_new() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
        let store_config = SqliteStoreConfig::new(Path::new("foo"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
        assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical() * 4);
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
        assert!(store_config.runtime_config.optimize);
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
        assert_eq!(store_config.runtime_config.cache_size, 2_000_000);
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
        assert_eq!(store_config.runtime_config.journal_size_limit, 10_000_000);
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
    fn test_with_low_memory_config() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
        let store_config = SqliteStoreConfig::with_low_memory_config(Path::new("foo"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
        assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical());
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
        assert!(store_config.runtime_config.optimize);
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
        assert_eq!(store_config.runtime_config.cache_size, 500_000);
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
        assert_eq!(store_config.runtime_config.journal_size_limit, 2_000_000);
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
    fn test_store_config_when_passphrase() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
        let store_config = SqliteStoreConfig::new(Path::new("foo"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
            .passphrase(Some("bar"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
            .pool_max_size(42)
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
            .optimize(false)
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
            .cache_size(43)
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
            .journal_size_limit(44);
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
        assert_eq!(store_config.path, PathBuf::from("foo"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
        assert_eq!(store_config.secret, Some(Secret::PassPhrase("bar".to_owned().into())));
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
        assert_eq!(store_config.pool_config.max_size, 42);
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
        assert!(store_config.runtime_config.optimize.not());
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
        assert_eq!(store_config.runtime_config.cache_size, 43);
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
        assert_eq!(store_config.runtime_config.journal_size_limit, 44);
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
    fn test_store_config_when_key() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
        let store_config = SqliteStoreConfig::new(Path::new("foo"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
            .key(Some(&[
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
                143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61,
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
                107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156,
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
            ]))
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
            .pool_max_size(42)
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
            .optimize(false)
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
            .cache_size(43)
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
            .journal_size_limit(44);
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
        assert_eq!(store_config.path, PathBuf::from("foo"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
        assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
            store_config.secret,
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
            Some(Secret::Key(Box::new([
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
                143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61,
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
                107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156,
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
            ])))
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
        assert_eq!(store_config.pool_config.max_size, 42);
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
        assert!(store_config.runtime_config.optimize.not());
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
        assert_eq!(store_config.runtime_config.cache_size, 43);
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
        assert_eq!(store_config.runtime_config.journal_size_limit, 44);
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
    fn test_store_config_path() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
        let store_config = SqliteStoreConfig::new(Path::new("foo")).path(Path::new("bar"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
        assert_eq!(store_config.path, PathBuf::from("bar"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
    fn test_pool_size_has_a_minimum() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
        let store_config = SqliteStoreConfig::new(Path::new("foo")).pool_max_size(1);
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
        assert_eq!(store_config.pool_config.max_size, POOL_MINIMUM_SIZE);
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
}