db_pool/
lib.rs

1//! [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) [![Book Status](https://github.com/yasamoka/db-pool/workflows/Test%20&%20Deploy/badge.svg)](https://yasamoka.github.io/db-pool) [![Crates.io](https://img.shields.io/crates/v/db-pool.svg)](https://crates.io/crates/db-pool)
2//!
3//! A thread-safe database pool for running database-tied tests in parallel with:
4//! - Easy setup
5//! - Proper isolation
6//! - Automatic creation, reuse, and cleanup
7//! - Async support
8//!
9//! ## Description
10//!
11//! Rather than simply providing a database connection pool that allows multiple connections to the same database, `db-pool` maintains a pool of separate isolated databases in order to allow running database-tied tests in parallel. It also handles the lifecycles of those databases: whenever you pick a database out of the pool, you can be sure that the database is clean and ready to be used. It ensures that databases are isolated so that no other tests are connected to the database you are using in any one test.
12//!
13//! ## Motivation
14//!
15//! When running tests against a database-tied service, such as a web server, a test database is generally used. However, this comes with its own set of difficulties:
16//!
17//! 1) The database has to be either (a) dropped and re-created or (b) cleaned before every test.
18//! 2) Tests have to run serially in order to avoid cross-contamination.
19//!
20//! This leads to several issues when running tests serially:
21//!
22//! - Test setup and teardown is now required.
23//! - Dropping and creating a database from scratch can be expensive.
24//! - Cleaning a database instead of dropping and re-creating one requires careful execution of dialect-specific statements.
25//!
26//! When switching to parallel execution of tests, even more difficulties arise:
27//!
28//! - Creating and dropping a database for each test can be expensive.
29//! - Sharing temporary databases across tests requires:
30//!   - isolating databases in concurrent use
31//!   - cleaning each database before reuse by a subsequent test
32//!   - restricting user privileges to prevent schema modification by rogue tests
33//!   - dropping temporary databases before or after a test run to reduce clutter
34//!
35//! `db-pool` takes care of all of these concerns while supporting multiple database types, backends, and connection pools.
36//!
37//! ### Databases
38//!
39//! - MySQL (MariaDB)
40//! - PostgreSQL
41//!
42//! ## Backends & Pools
43//!
44//! ### Sync
45//!
46//! | Backend                                               | Pool                                      | Feature           |
47//! | ----------------------------------------------------- | ----------------------------------------- | ----------------- |
48//! | [diesel/mysql](struct@sync::DieselMySQLBackend)       | [r2d2](https://docs.rs/r2d2/0.8.10/r2d2/) | `diesel-mysql`    |
49//! | [diesel/postgres](struct@sync::DieselPostgresBackend) | [r2d2](https://docs.rs/r2d2/0.8.10/r2d2/) | `diesel-postgres` |
50//! | [mysql](struct@sync::MySQLBackend)                    | [r2d2](https://docs.rs/r2d2/0.8.10/r2d2/) | `mysql`           |
51//! | [postgres](struct@sync::PostgresBackend)              | [r2d2](https://docs.rs/r2d2/0.8.10/r2d2/) | `postgres`        |
52//!
53//! ### Async
54//!
55//! | Backend                                                           | Pool                                                                                      | Features                                    |
56//! | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
57//! | [diesel-async/mysql](struct@async::DieselAsyncMySQLBackend)       | [bb8](https://docs.rs/diesel-async/0.5.2/diesel_async/pooled_connection/bb8/index.html)   | `diesel-async-mysql`, `diesel-async-bb8`    |
58//! | [diesel-async/mysql](struct@async::DieselAsyncMySQLBackend)       | [mobc](https://docs.rs/diesel-async/0.5.2/diesel_async/pooled_connection/mobc/index.html) | `diesel-async-mysql`, `diesel-async-mobc`   |
59//! | [diesel-async/postgres](struct@async::DieselAsyncPostgresBackend) | [bb8](https://docs.rs/diesel-async/0.5.2/diesel_async/pooled_connection/bb8/index.html)   | `diesel-async-postgres`, `diesel-async-bb8` |
60//! | [diesel-async/postgres](struct@async::DieselAsyncPostgresBackend) | [mobc](https://docs.rs/diesel-async/0.5.2/diesel_async/pooled_connection/mobc/index.html) | `diesel-async-postgres`, `diesel-async-bb8` |
61//! | [sea-orm/sqlx-mysql](struct@async::SeaORMMySQLBackend)            | [sqlx](https://docs.rs/sqlx/0.8.6/sqlx/struct.Pool.html)                                  | `sea-orm-mysql`                             |
62//! | [sea-orm/sqlx-postgres](struct@async::SeaORMPostgresBackend)      | [sqlx](https://docs.rs/sqlx/0.8.6/sqlx/struct.Pool.html)                                  | `sea-orm-postgres`                          |
63//! | [sqlx/mysql](struct@async::SqlxMySQLBackend)                      | [sqlx](https://docs.rs/sqlx/0.8.6/sqlx/struct.Pool.html)                                  | `sqlx-mysql`                                |
64//! | [sqlx/postgres](struct@async::SqlxPostgresBackend)                | [sqlx](https://docs.rs/sqlx/0.8.6/sqlx/struct.Pool.html)                                  | `sqlx-postgres`                             |
65//! | [tokio-postgres](struct@async::TokioPostgresBackend)              | [bb8](https://docs.rs/bb8-postgres/0.8.1/bb8_postgres/)                                   | `tokio-postgres`, `tokio-postgres-bb8`      |
66//! | [tokio-postgres](struct@async::TokioPostgresBackend)              | [mobc](https://docs.rs/mobc-postgres/0.8.0/mobc_postgres/)                                | `tokio-postgres`, `tokio-postgres-mobc`     |
67
68#![doc(
69    html_favicon_url = "https://raw.githubusercontent.com/yasamoka/db-pool/main/logo.svg",
70    html_logo_url = "https://raw.githubusercontent.com/yasamoka/db-pool/main/logo.svg",
71    issue_tracker_base_url = "https://github.com/yasamoka/db-pool/issues"
72)]
73#![forbid(unsafe_code)]
74#![deny(
75    missing_docs,
76    clippy::cargo,
77    clippy::complexity,
78    clippy::correctness,
79    clippy::pedantic,
80    clippy::perf,
81    clippy::style,
82    clippy::suspicious,
83    clippy::unwrap_used
84)]
85#![allow(
86    clippy::module_name_repetitions,
87    clippy::module_inception,
88    clippy::missing_errors_doc
89)]
90
91mod common;
92
93/// Async backends
94#[cfg(feature = "_async")]
95pub mod r#async;
96/// Sync backends
97#[cfg(feature = "_sync")]
98pub mod sync;
99mod util;
100
101#[allow(unused_imports)]
102pub use common::config::*;
103
104#[cfg(test)]
105mod tests {
106    #![allow(clippy::unwrap_used)]
107
108    use std::sync::OnceLock;
109
110    use dotenvy::dotenv;
111    use tokio::sync::RwLock;
112
113    use crate::common::config::{mysql::PrivilegedMySQLConfig, postgres::PrivilegedPostgresConfig};
114
115    #[cfg(feature = "_mysql")]
116    pub static MYSQL_DROP_LOCK: RwLock<()> = RwLock::const_new(());
117
118    #[cfg(feature = "_postgres")]
119    pub static PG_DROP_LOCK: RwLock<()> = RwLock::const_new(());
120
121    pub fn get_privileged_mysql_config() -> &'static PrivilegedMySQLConfig {
122        static CONFIG: OnceLock<PrivilegedMySQLConfig> = OnceLock::new();
123        CONFIG.get_or_init(|| {
124            dotenv().ok();
125            PrivilegedMySQLConfig::from_env().unwrap()
126        })
127    }
128
129    pub fn get_privileged_postgres_config() -> &'static PrivilegedPostgresConfig {
130        static CONFIG: OnceLock<PrivilegedPostgresConfig> = OnceLock::new();
131        CONFIG.get_or_init(|| {
132            dotenv().ok();
133            PrivilegedPostgresConfig::from_env().unwrap()
134        })
135    }
136}