#[cfg(feature = "postgres")]
use sqlx::postgres::{PgPool, PgPoolOptions};
#[cfg(feature = "sqlite")]
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
use tokio::sync::Mutex;
use crate::error::FlozError;
use super::placeholder::replace_pg_placeholders;
use super::tx::{Tx, TxInner};
#[derive(Debug, Clone)]
pub(crate) enum DbInner {
#[cfg(feature = "postgres")]
Postgres(PgPool),
#[cfg(feature = "sqlite")]
Sqlite(SqlitePool),
}
#[derive(Debug, Clone)]
pub struct Db {
pub(crate) inner: DbInner,
}
impl Db {
pub async fn connect(url: &str) -> Result<Self, FlozError> {
#[cfg(feature = "sqlite")]
if url.starts_with("sqlite:") {
let pool = SqlitePoolOptions::new()
.max_connections(5)
.connect(url)
.await?;
return Ok(Self {
inner: DbInner::Sqlite(pool),
});
}
#[cfg(feature = "postgres")]
if url.starts_with("postgres:") || url.starts_with("postgresql:") {
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(url)
.await?;
return Ok(Self {
inner: DbInner::Postgres(pool),
});
}
Err(FlozError::Database(sqlx::Error::Configuration(
format!(
"Unsupported database URL scheme: {}. Enable the `postgres` or `sqlite` feature.",
url
)
.into(),
)))
}
pub async fn connect_env(env_var: &str, default_url: &str) -> Result<Self, FlozError> {
let url = std::env::var(env_var).unwrap_or_else(|_| default_url.to_string());
Self::connect(&url).await
}
#[cfg(feature = "postgres")]
pub fn from_pg_pool(pool: PgPool) -> Self {
Self {
inner: DbInner::Postgres(pool),
}
}
#[cfg(feature = "sqlite")]
pub fn from_sqlite_pool(pool: SqlitePool) -> Self {
Self {
inner: DbInner::Sqlite(pool),
}
}
#[allow(unreachable_patterns)]
pub async fn begin(&self) -> Result<Tx, FlozError> {
match &self.inner {
#[cfg(feature = "postgres")]
DbInner::Postgres(pool) => {
let mut conn = pool.acquire().await?;
sqlx::query("BEGIN").execute(&mut *conn).await?;
Ok(Tx {
inner: TxInner::Postgres(Mutex::new(conn)),
committed: false,
})
}
#[cfg(feature = "sqlite")]
DbInner::Sqlite(pool) => {
let mut conn = pool.acquire().await?;
sqlx::query("BEGIN").execute(&mut *conn).await?;
Ok(Tx {
inner: TxInner::Sqlite(Mutex::new(conn)),
committed: false,
})
}
_ => unreachable!("no database backend enabled"),
}
}
#[allow(unreachable_patterns)]
pub(crate) fn adjust_sql(&self, sql: &str) -> String {
match &self.inner {
#[cfg(feature = "postgres")]
DbInner::Postgres(_) => sql.to_string(),
#[cfg(feature = "sqlite")]
DbInner::Sqlite(_) => replace_pg_placeholders(sql),
_ => unreachable!("no database backend enabled"),
}
}
}