#[cfg(any(feature = "db-sqlite", feature = "db-postgres", feature = "db-mysql"))]
use sqlx::Pool as SqlxPool;
#[cfg(feature = "db-mysql")]
use sqlx::{MySql, mysql::MySqlPoolOptions};
#[cfg(feature = "db-postgres")]
use sqlx::{Postgres, postgres::PgPoolOptions};
#[cfg(feature = "db-sqlite")]
use sqlx::{Sqlite, sqlite::SqlitePoolOptions};
use crate::db::{DatabaseConfig, DatabaseError, DatabaseKind, DatabaseResult};
#[cfg(feature = "db-sqlite")]
pub type SqliteDatabasePool = SqlxPool<Sqlite>;
#[cfg(feature = "db-postgres")]
pub type PostgresDatabasePool = SqlxPool<Postgres>;
#[cfg(feature = "db-mysql")]
pub type MySqlDatabasePool = SqlxPool<MySql>;
#[cfg(feature = "db-sqlite")]
pub type DatabasePool = SqliteDatabasePool;
#[cfg(all(not(feature = "db-sqlite"), feature = "db-postgres"))]
pub type DatabasePool = PostgresDatabasePool;
#[cfg(all(
not(feature = "db-sqlite"),
not(feature = "db-postgres"),
feature = "db-mysql"
))]
pub type DatabasePool = MySqlDatabasePool;
#[cfg(feature = "db-sqlite")]
pub async fn connect_pool(config: &DatabaseConfig) -> DatabaseResult<DatabasePool> {
connect_sqlite_pool(config).await
}
#[cfg(all(not(feature = "db-sqlite"), feature = "db-postgres"))]
pub async fn connect_pool(config: &DatabaseConfig) -> DatabaseResult<DatabasePool> {
connect_postgres_pool(config).await
}
#[cfg(all(
not(feature = "db-sqlite"),
not(feature = "db-postgres"),
feature = "db-mysql"
))]
pub async fn connect_pool(config: &DatabaseConfig) -> DatabaseResult<DatabasePool> {
connect_mysql_pool(config).await
}
#[cfg(feature = "db-sqlite")]
pub async fn connect_sqlite_pool(config: &DatabaseConfig) -> DatabaseResult<SqliteDatabasePool> {
if config.kind != DatabaseKind::Sqlite {
return Err(DatabaseError::Unsupported(
"sqlite database kind required".to_string(),
));
}
Ok(SqlitePoolOptions::new()
.max_connections(config.max_connections)
.acquire_timeout(config.connect_timeout)
.connect(&config.url)
.await?)
}
#[cfg(feature = "db-postgres")]
pub async fn connect_postgres_pool(
config: &DatabaseConfig,
) -> DatabaseResult<PostgresDatabasePool> {
if config.kind != DatabaseKind::Postgres {
return Err(DatabaseError::Unsupported(
"postgres database kind required".to_string(),
));
}
Ok(PgPoolOptions::new()
.max_connections(config.max_connections)
.acquire_timeout(config.connect_timeout)
.connect(&config.url)
.await?)
}
#[cfg(feature = "db-mysql")]
pub async fn connect_mysql_pool(config: &DatabaseConfig) -> DatabaseResult<MySqlDatabasePool> {
if config.kind != DatabaseKind::Mysql {
return Err(DatabaseError::Unsupported(
"mysql database kind required".to_string(),
));
}
Ok(MySqlPoolOptions::new()
.max_connections(config.max_connections)
.acquire_timeout(config.connect_timeout)
.connect(&config.url)
.await?)
}
pub async fn health_check(pool: &DatabasePool) -> DatabaseResult<()> {
sqlx::query("SELECT 1").execute(pool).await?;
Ok(())
}
#[cfg(feature = "observability")]
pub async fn health_check_with_metrics(
pool: &DatabasePool,
metrics: &crate::observability::MetricsRegistry,
) -> DatabaseResult<()> {
crate::observability::observe_sql_query(
Some(metrics),
default_database_kind_label(),
"db",
"health_check",
"select",
health_check(pool),
)
.await
}
#[cfg(feature = "db-postgres")]
pub async fn health_check_postgres(pool: &PostgresDatabasePool) -> DatabaseResult<()> {
sqlx::query("SELECT 1").execute(pool).await?;
Ok(())
}
#[cfg(feature = "db-mysql")]
pub async fn health_check_mysql(pool: &MySqlDatabasePool) -> DatabaseResult<()> {
sqlx::query("SELECT 1").execute(pool).await?;
Ok(())
}
#[cfg(all(feature = "observability", feature = "db-sqlite"))]
fn default_database_kind_label() -> &'static str {
"sqlite"
}
#[cfg(all(
feature = "observability",
not(feature = "db-sqlite"),
feature = "db-postgres"
))]
fn default_database_kind_label() -> &'static str {
"postgres"
}
#[cfg(all(
feature = "observability",
not(feature = "db-sqlite"),
not(feature = "db-postgres"),
feature = "db-mysql"
))]
fn default_database_kind_label() -> &'static str {
"mysql"
}
#[cfg(test)]
mod tests {
use crate::db::{DatabaseConfig, DatabaseKind};
#[cfg(feature = "db-sqlite")]
use crate::db::{connect_pool, health_check};
#[cfg(feature = "db-sqlite")]
#[tokio::test]
async fn sqlite_pool_passes_health_check() {
let pool = connect_pool(&DatabaseConfig::default())
.await
.expect("pool");
health_check(&pool).await.expect("health");
}
#[test]
fn postgres_config_uses_postgres_kind() {
let config = DatabaseConfig::postgres("postgres://localhost/app");
assert_eq!(config.kind, DatabaseKind::Postgres);
}
#[test]
fn mysql_config_uses_mysql_kind() {
let config = DatabaseConfig::mysql("mysql://localhost/app");
assert_eq!(config.kind, DatabaseKind::Mysql);
}
}