Replace literal SQL with Diesel abstractions

This commit is contained in:
2026-07-18 17:00:32 +02:00
parent a727c9073d
commit ca5eb4e1ac
69 changed files with 3508 additions and 4285 deletions

View File

@@ -1,37 +1,88 @@
use crate::db::migrations;
use rusqlite::Connection;
use std::cell::RefCell;
use std::path::Path;
use diesel::connection::SimpleConnection;
use diesel::prelude::*;
use crate::db::migrations;
#[derive(Debug, thiserror::Error)]
pub enum DatabaseError {
#[error("{0}")]
Connection(#[from] diesel::ConnectionError),
#[error("{0}")]
Query(#[from] diesel::result::Error),
}
/// Shared synchronous Diesel connection used by the engine query API.
pub struct DbConnection(RefCell<SqliteConnection>);
impl DbConnection {
pub fn with<T>(
&self,
operation: impl FnOnce(&mut SqliteConnection) -> diesel::QueryResult<T>,
) -> diesel::QueryResult<T> {
operation(&mut self.0.borrow_mut())
}
pub(crate) fn with_migrations<T>(
&self,
operation: impl FnOnce(&mut SqliteConnection) -> T,
) -> T {
operation(&mut self.0.borrow_mut())
}
pub(crate) fn begin_savepoint(&self) -> diesel::QueryResult<()> {
self.0.borrow_mut().batch_execute("SAVEPOINT bds_operation")
}
pub(crate) fn release_savepoint(&self) -> diesel::QueryResult<()> {
self.0.borrow_mut().batch_execute("RELEASE bds_operation")
}
pub(crate) fn rollback_savepoint(&self) -> diesel::QueryResult<()> {
self.0
.borrow_mut()
.batch_execute("ROLLBACK TO bds_operation; RELEASE bds_operation")
}
}
/// Database wrapper managing a SQLite connection.
pub struct Database {
conn: Connection,
conn: DbConnection,
}
impl Database {
/// Open an existing bDS project database.
pub fn open(path: &Path) -> Result<Self, rusqlite::Error> {
let conn = Connection::open(path)?;
conn.execute_batch(
"PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA foreign_keys=ON;",
)?;
Ok(Self { conn })
pub fn open(path: &Path) -> Result<Self, DatabaseError> {
Self::establish(path.to_string_lossy().as_ref(), true)
}
/// Open an in-memory database (for tests).
pub fn open_in_memory() -> Result<Self, rusqlite::Error> {
let conn = Connection::open_in_memory()?;
conn.execute_batch("PRAGMA foreign_keys=ON;")?;
Ok(Self { conn })
pub fn open_in_memory() -> Result<Self, DatabaseError> {
Self::establish(":memory:", false)
}
/// Get a reference to the underlying connection.
pub fn conn(&self) -> &Connection {
fn establish(database_url: &str, wal: bool) -> Result<Self, DatabaseError> {
let mut conn = SqliteConnection::establish(database_url)?;
// SQLite connection configuration is backend-specific and not expressible in Diesel's DSL.
conn.batch_execute(if wal {
"PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA foreign_keys=ON;"
} else {
"PRAGMA foreign_keys=ON;"
})?;
Ok(Self {
conn: DbConnection(RefCell::new(conn)),
})
}
pub fn conn(&self) -> &DbConnection {
&self.conn
}
/// Run all pending migrations via refinery.
pub fn migrate(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
migrations::run_migrations(&mut self.conn)
/// Run all pending embedded Diesel migrations.
pub fn migrate(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
migrations::run_migrations(&self.conn)
}
}
@@ -42,9 +93,11 @@ mod tests {
#[test]
fn open_in_memory() {
let db = Database::open_in_memory().expect("should open in-memory db");
let result: i64 = db
let result = db
.conn()
.query_row("SELECT 1", [], |row| row.get(0))
.with(|conn| {
diesel::select(1.into_sql::<diesel::sql_types::Integer>()).get_result::<i32>(conn)
})
.unwrap();
assert_eq!(result, 1);
}