//! The SQLite pool. Every connection sets `foreign_keys = ON`, `journal_mode = WAL`, `synchronous = NORMAL` and //! `busy_timeout = 5000` (SPEC §8.9). use std::path::Path; use std::time::Duration; use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions, SqliteSynchronous}; use sqlx::{Sqlite, SqlitePool, Transaction}; use crate::error::AppError; /// The pool type used everywhere. pub type Db = SqlitePool; /// `busy_timeout` (SPEC §8.9). pub const BUSY_TIMEOUT: Duration = Duration::from_millis(5000); /// At most this many connections; two users and one writer at a time need few. const MAX_CONNECTIONS: u32 = 4; /// The connect options for the database file at `path` (created if missing). pub fn options(path: &Path) -> SqliteConnectOptions { SqliteConnectOptions::new() .filename(path) .create_if_missing(true) .foreign_keys(true) .journal_mode(SqliteJournalMode::Wal) .synchronous(SqliteSynchronous::Normal) .busy_timeout(BUSY_TIMEOUT) } /// Opens the pool for the database file at `path`. The parent directory must exist. pub async fn connect(path: &Path) -> Result { let pool = SqlitePoolOptions::new() .max_connections(MAX_CONNECTIONS) .acquire_timeout(BUSY_TIMEOUT) .connect_with(options(path)) .await?; Ok(pool) } /// Starts a write transaction with `BEGIN IMMEDIATE` (SPEC §14.1: every write transaction does). pub async fn begin_immediate(db: &Db) -> Result, AppError> { Ok(db.begin_with("BEGIN IMMEDIATE").await?) }