use crate::cli::DatabaseType;
use crate::error::{CliError, CliResult};
use sqlx::{AnyPool, Row};
pub struct Database {
pub pool: AnyPool,
pub db_type: DatabaseType,
}
impl Database {
pub async fn connect(url: &str) -> CliResult<Self> {
let db_type = Self::detect_type(url)?;
sqlx::any::install_default_drivers();
let pool = AnyPool::connect(url).await?;
Ok(Self { pool, db_type })
}
pub fn detect_type(url: &str) -> CliResult<DatabaseType> {
if url.starts_with("sqlite:") {
Ok(DatabaseType::Sqlite)
} else if url.starts_with("postgres:") || url.starts_with("postgresql:") {
Ok(DatabaseType::Postgres)
} else {
Err(CliError::UnknownDatabase(url.to_string()))
}
}
pub async fn count_rows(&self, table: &str) -> CliResult<i64> {
let query = format!("SELECT COUNT(*) as count FROM {}", table);
let row = sqlx::query(&query).fetch_one(&self.pool).await?;
let count: i64 = row.get("count");
Ok(count)
}
pub async fn table_exists(&self, table: &str) -> CliResult<bool> {
let result = match self.db_type {
DatabaseType::Sqlite => {
let query = "SELECT name FROM sqlite_master WHERE type='table' AND name = $1";
sqlx::query(query)
.bind(table)
.fetch_optional(&self.pool)
.await?
}
DatabaseType::Postgres => {
let query = "SELECT tablename FROM pg_tables WHERE tablename = $1";
sqlx::query(query)
.bind(table)
.fetch_optional(&self.pool)
.await?
}
};
Ok(result.is_some())
}
pub async fn drop_table(&self, table: &str) -> CliResult<()> {
let query = match self.db_type {
DatabaseType::Sqlite => format!("DROP TABLE IF EXISTS {}", table),
DatabaseType::Postgres => format!("DROP TABLE IF EXISTS {} CASCADE", table),
};
sqlx::query(&query).execute(&self.pool).await?;
Ok(())
}
}