use crate::drivers::{Driver, DriverMutConnectionDeref};
use diesel::connection::{AnsiTransactionManager, SimpleConnection, TransactionManager};
use diesel::prelude::*;
use diesel::result;
use std::path::Path;
pub type DieselConnectionPool = crate::SyncConnectionPool<DieselDriver>;
pub type DieselPooledConnection = crate::SyncPooledConnection<DieselDriver>;
pub type DieselConnectionPoolError = crate::ConnectionPoolError<<DieselDriver as Driver>::Error>;
pub type DieselTransaction<'t> = crate::Transaction<'t, DieselDriver>;
pub type DieselReadTransaction<'t> = crate::ReadTransaction<'t, DieselDriver>;
#[cfg(feature = "async")]
pub type DieselAsyncConnectionPool = crate::AsyncConnectionPool<DieselDriver>;
#[cfg(feature = "async")]
pub type DieselAsyncPooledConnection = crate::AsyncPooledConnection<DieselDriver>;
#[cfg(feature = "async")]
pub type DieselAsyncTransaction<'t> = crate::AsyncTransaction<'t, DieselDriver>;
#[cfg(feature = "async")]
pub type DieselAsyncReadTransaction<'t> = crate::AsyncReadTransaction<'t, DieselDriver>;
#[cfg(feature = "async")]
pub type DieselAsyncConnectionError = crate::AsyncConnectionError<<DieselDriver as Driver>::Error>;
#[derive(Debug)]
pub struct DieselDriver;
impl Driver for DieselDriver {
type Connection = SqliteConnection;
type Error = result::Error;
type ConnectionError = ConnectionError;
type ConnectionRef<'c> = &'c mut Self::Connection;
fn new_read_connection(path: &Path) -> Result<Self::Connection, Self::ConnectionError> {
SqliteConnection::establish(&Self::file_uri_read_only(path))
}
fn new_write_connection(path: &Path) -> Result<Self::Connection, Self::ConnectionError> {
let mut connection = SqliteConnection::establish(&Self::file_uri(path))?;
Self::apply_pragmas(&mut connection)
.map_err(Self::ConnectionError::CouldntSetupConfiguration)?;
Ok(connection)
}
fn begin_transaction(connection: &mut Self::Connection, sql: &str) -> Result<(), Self::Error> {
AnsiTransactionManager::begin_transaction_sql(connection, sql)
}
fn commit_transaction(connection: &mut Self::Connection) -> Result<(), Self::Error> {
AnsiTransactionManager::commit_transaction(connection)
}
fn rollback_transaction(connection: &mut Self::Connection) -> Result<(), Self::Error> {
AnsiTransactionManager::rollback_transaction(connection)
}
}
impl DriverMutConnectionDeref for DieselDriver {}
impl DieselDriver {
fn file_uri(path: &Path) -> String {
format!("file://{}", path.to_string_lossy())
}
fn file_uri_read_only(path: &Path) -> String {
format!("file://{}?mode=ro", path.to_string_lossy())
}
fn apply_pragmas(conn: &mut SqliteConnection) -> QueryResult<()> {
conn.batch_execute("PRAGMA journal_mode = WAL;")?;
conn.batch_execute("PRAGMA foreign_keys = ON;")?;
conn.batch_execute("PRAGMA synchronous= FULL;")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
ConnectionPool, ConnectionPoolConfig, ReadTransaction, SyncConnectionPool, Transaction,
};
use diesel::result::Error;
use diesel::{QueryResult, RunQueryDsl, Selectable, insert_into, sql_query};
use sqlite_watcher::watcher::Watcher;
use std::sync::Arc;
use tempdir::TempDir;
#[derive(Insertable, Queryable, Selectable)]
#[diesel(table_name = foo)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
struct Foo {
pub id: i32,
}
fn create_table(tx: &mut Transaction<'_, DieselDriver>) -> QueryResult<()> {
sql_query("CREATE TABLE foo (id INTEGER PRIMARY KEY)").execute(&mut **tx)?;
insert_into(foo::table)
.values(Foo { id: 1 })
.execute(&mut **tx)?;
Ok(())
}
fn read_value(conn: &mut ReadTransaction<'_, DieselDriver>) -> QueryResult<Foo> {
foo::dsl::foo
.find(1)
.select(Foo::as_select())
.first(&mut **conn)
}
#[test]
fn read_scope_query() {
let (pool, _dir) = new_pool();
let mut conn = pool.connection().unwrap();
conn.transaction_closure(create_table).unwrap();
let foo = conn.read_transaction_closure(read_value).unwrap();
assert_eq!(foo.id, 1);
}
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: DatabaseError(Unknown, \"attempt to write a readonly database\")"
)]
fn panic_on_write_in_read_scope() {
let (pool, _dir) = new_pool();
let mut conn = pool.connection().unwrap();
conn.transaction_closure(|tx| {
sql_query("CREATE TABLE foo (id INTEGER PRIMARY KEY)")
.execute(&mut **tx)
.unwrap();
Ok::<_, Error>(())
})
.unwrap();
conn.read_transaction_closure(|conn| {
insert_into(foo::table)
.values(Foo { id: 1 })
.execute(&mut **conn)
})
.unwrap();
}
fn new_pool() -> (Arc<SyncConnectionPool<DieselDriver>>, TempDir) {
let dir = tempdir::TempDir::new("diesel-test").unwrap();
let pool = ConnectionPool::new(ConnectionPoolConfig {
max_read_connection_count: 4,
file_path: dir.path().join("sqlite.db"),
connection_acquire_timeout: None,
#[cfg(feature = "watcher")]
watcher: Watcher::new().unwrap(),
})
.unwrap();
(pool, dir)
}
diesel::table! {
foo (id) {
id -> Integer,
}
}
}