//! Database access. All SQL lives in this crate. pub mod audit; pub mod authority; pub mod catalog; pub mod fields; pub mod seed; pub mod users; pub mod vocab; use sqlx::postgres::{PgPool, PgPoolOptions}; /// Result of a delete that catalogue-object references may block. #[derive(Debug, PartialEq, Eq)] pub enum DeleteOutcome { /// The row was deleted. Deleted, /// Refused: `count` catalogue objects still reference it. InUse { count: i64 }, /// The row did not exist. NotFound, } /// A handle to the organization's PostgreSQL database. #[derive(Clone)] pub struct Db { pool: PgPool, } impl Db { /// Connect to the database at `database_url`, opening a connection pool with at most /// `max_connections` connections. pub async fn connect(database_url: &str, max_connections: u32) -> Result { let pool = PgPoolOptions::new() .max_connections(max_connections) .connect(database_url) .await?; Ok(Self { pool }) } /// Build a handle from an existing pool (used in tests). pub fn from_pool(pool: PgPool) -> Self { Self { pool } } /// Borrow the underlying pool for repository modules. pub fn pool(&self) -> &PgPool { &self.pool } /// Readiness check: run a trivial query to confirm the database answers. pub async fn ping(&self) -> Result<(), sqlx::Error> { sqlx::query_scalar::<_, i32>("SELECT 1") .fetch_one(&self.pool) .await?; Ok(()) } /// Apply all pending schema migrations (embedded at compile time). /// /// Pre-1.0 the migration files are rewritten freely and dev databases are /// recreated; this is the schema-bootstrap mechanism, not forward-migration /// discipline. pub async fn migrate(&self) -> Result<(), sqlx::migrate::MigrateError> { sqlx::migrate!().run(&self.pool).await } }