Replace literal SQL with Diesel abstractions
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user