rocket_sync_db_pools/
lib.rs

1//! Traits, utilities, and a macro for easy database connection pooling.
2//!
3//! # Overview
4//!
5//! This crate provides traits, utilities, and a procedural macro for
6//! configuring and accessing database connection pools in Rocket. A _database
7//! connection pool_ is a data structure that maintains active database
8//! connections for later use in the application. This implementation is backed
9//! by [`r2d2`] and exposes connections through request guards.
10//!
11//! Databases are individually configured through Rocket's regular configuration
12//! mechanisms. Connecting a Rocket application to a database using this library
13//! occurs in three simple steps:
14//!
15//!   1. Configure your databases in `Rocket.toml`.
16//!      (see [Configuration](#configuration))
17//!   2. Associate a request guard type and fairing with each database.
18//!      (see [Guard Types](#guard-types))
19//!   3. Use the request guard to retrieve a connection in a handler.
20//!      (see [Handlers](#handlers))
21//!
22//! For a list of supported databases, see [Provided Databases](#provided). This
23//! support can be easily extended by implementing the [`Poolable`] trait. See
24//! [Extending](#extending) for more.
25//!
26//! ## Example
27//!
28//! Before using this library, the feature corresponding to your database type
29//! in `rocket_sync_db_pools` must be enabled:
30//!
31//! ```toml
32//! [dependencies.rocket_sync_db_pools]
33//! version = "0.1.0"
34//! features = ["diesel_sqlite_pool"]
35//! ```
36//!
37//! See [Provided](#provided) for a list of supported database and their
38//! associated feature name.
39//!
40//! In whichever configuration source you choose, configure a `databases`
41//! dictionary with an internal dictionary for each database, here `sqlite_logs`
42//! in a TOML source:
43//!
44//! ```toml
45//! [default.databases]
46//! sqlite_logs = { url = "/path/to/database.sqlite" }
47//! ```
48//!
49//! In your application's source code, one-time:
50//!
51//! ```rust
52//! # #[macro_use] extern crate rocket;
53//! # #[cfg(feature = "diesel_sqlite_pool")]
54//! # mod test {
55//! use rocket_sync_db_pools::{database, diesel};
56//!
57//! #[database("sqlite_logs")]
58//! struct LogsDbConn(diesel::SqliteConnection);
59//!
60//! #[launch]
61//! fn rocket() -> _ {
62//!     rocket::build().attach(LogsDbConn::fairing())
63//! }
64//! # } fn main() {}
65//! ```
66//!
67//! Whenever a connection to the database is needed:
68//!
69//! ```rust
70//! # #[macro_use] extern crate rocket;
71//! # #[macro_use] extern crate rocket_sync_db_pools;
72//! #
73//! # #[cfg(feature = "diesel_sqlite_pool")]
74//! # mod test {
75//! # use rocket_sync_db_pools::diesel;
76//! #
77//! # #[database("sqlite_logs")]
78//! # struct LogsDbConn(diesel::SqliteConnection);
79//! #
80//! # type Logs = ();
81//! # type Result<T> = std::result::Result<T, ()>;
82//! #
83//! #[get("/logs/<id>")]
84//! async fn get_logs(conn: LogsDbConn, id: usize) -> Result<Logs> {
85//! # /*
86//!     conn.run(|c| Logs::by_id(c, id)).await
87//! # */
88//! # Ok(())
89//! }
90//! # } fn main() {}
91//! ```
92//!
93//! # Usage
94//!
95//! ## Configuration
96//!
97//! Databases can be configured as any other values. Using the default
98//! configuration provider, either via `Rocket.toml` or environment variables.
99//! You can also use a custom provider.
100//!
101//! ### `Rocket.toml`
102//!
103//! To configure a database via `Rocket.toml`, add a table for each database to
104//! the `databases` table where the key is a name of your choice. The table
105//! should have a `url` key and, optionally, `pool_size` and `timeout` keys.
106//! This looks as follows:
107//!
108//! ```toml
109//! # Option 1:
110//! [global.databases]
111//! sqlite_db = { url = "db.sqlite" }
112//!
113//! # Option 2:
114//! [global.databases.my_db]
115//! url = "postgres://root:root@localhost/my_db"
116//!
117//! # With `pool_size` and `timeout` keys:
118//! [global.databases.sqlite_db]
119//! url = "db.sqlite"
120//! pool_size = 20
121//! timeout = 5
122//! ```
123//!
124//! The table _requires_ one key:
125//!
126//!   * `url` - the URl to the database
127//!
128//! Additionally, all configurations accept the following _optional_ keys:
129//!
130//!   * `pool_size` - the size of the pool, i.e., the number of connections to
131//!     pool (defaults to the configured number of workers * 4)
132//!   * `timeout` - max number of seconds to wait for a connection to become
133//!     available (defaults to `5`)
134//!
135//! Additional options may be required or supported by other adapters.
136//!
137//! ### Procedurally
138//!
139//! Databases can also be configured procedurally via `rocket::custom()`.
140//! The example below does just this:
141//!
142//! ```rust
143//! # #[cfg(feature = "diesel_sqlite_pool")] {
144//! # use rocket::launch;
145//! use rocket::figment::{value::{Map, Value}, util::map};
146//!
147//! #[launch]
148//! fn rocket() -> _ {
149//!     let db: Map<_, Value> = map! {
150//!         "url" => "db.sqlite".into(),
151//!         "pool_size" => 10.into(),
152//!         "timeout" => 5.into(),
153//!     };
154//!
155//!     let figment = rocket::Config::figment()
156//!         .merge(("databases", map!["my_db" => db]));
157//!
158//!     rocket::custom(figment)
159//! }
160//! # rocket();
161//! # }
162//! ```
163//!
164//! ### Environment Variables
165//!
166//! Lastly, databases can be configured via environment variables by specifying
167//! the `databases` table as detailed in the [Environment Variables
168//! configuration
169//! guide](https://rocket.rs/v0.5/guide/configuration/#environment-variables):
170//!
171//! ```bash
172//! ROCKET_DATABASES='{my_db={url="db.sqlite"}}'
173//! ```
174//!
175//! Multiple databases can be specified in the `ROCKET_DATABASES` environment variable
176//! as well by comma separating them:
177//!
178//! ```bash
179//! ROCKET_DATABASES='{my_db={url="db.sqlite"},my_pg_db={url="postgres://root:root@localhost/my_pg_db"}}'
180//! ```
181//!
182//! ## Guard Types
183//!
184//! Once a database has been configured, the `#[database]` attribute can be used
185//! to tie a type in your application to a configured database. The database
186//! attribute accepts a single string parameter that indicates the name of the
187//! database. This corresponds to the database name set as the database's
188//! configuration key.
189//!
190//! See [`ExampleDb`](example::ExampleDb) for everything that the macro
191//! generates. Specifically, it generates:
192//!
193//!   * A [`FromRequest`] implementation for the decorated type.
194//!   * A [`Sentinel`](rocket::Sentinel) implementation for the decorated type.
195//!   * A [`fairing()`](example::ExampleDb::fairing()) method to initialize the
196//!     database.
197//!   * A [`run()`](example::ExampleDb::run()) method to execute blocking
198//!     database operations in an `async`-safe manner.
199//!   * A [`pool()`](example::ExampleDb::pool()) method to retrieve the
200//!     backing connection pool.
201//!
202//! The attribute can only be applied to tuple structs with one field. The
203//! internal type of the structure must implement [`Poolable`].
204//!
205//! ```rust
206//! # #[macro_use] extern crate rocket_sync_db_pools;
207//! # #[cfg(feature = "diesel_sqlite_pool")]
208//! # mod test {
209//! use rocket_sync_db_pools::diesel;
210//!
211//! #[database("my_db")]
212//! struct MyDatabase(diesel::SqliteConnection);
213//! # }
214//! ```
215//!
216//! Other databases can be used by specifying their respective [`Poolable`]
217//! type:
218//!
219//! ```rust
220//! # #[macro_use] extern crate rocket_sync_db_pools;
221//! # #[cfg(feature = "postgres_pool")]
222//! # mod test {
223//! use rocket_sync_db_pools::postgres;
224//!
225//! #[database("my_pg_db")]
226//! struct MyPgDatabase(postgres::Client);
227//! # }
228//! ```
229//!
230//! The fairing returned from the generated `fairing()` method _must_ be
231//! attached for the request guard implementation to succeed. Putting the pieces
232//! together, a use of the `#[database]` attribute looks as follows:
233//!
234//! ```rust
235//! # #[macro_use] extern crate rocket;
236//! # #[macro_use] extern crate rocket_sync_db_pools;
237//! #
238//! # #[cfg(feature = "diesel_sqlite_pool")] {
239//! # use rocket::figment::{value::{Map, Value}, util::map};
240//! use rocket_sync_db_pools::diesel;
241//!
242//! #[database("my_db")]
243//! struct MyDatabase(diesel::SqliteConnection);
244//!
245//! #[launch]
246//! fn rocket() -> _ {
247//! #   let db: Map<_, Value> = map![
248//! #        "url" => "db.sqlite".into(), "pool_size" => 10.into()
249//! #   ];
250//! #   let figment = rocket::Config::figment().merge(("databases", map!["my_db" => db]));
251//!     rocket::custom(figment).attach(MyDatabase::fairing())
252//! }
253//! # }
254//! ```
255//!
256//! ## Handlers
257//!
258//! Finally, use your type as a request guard in a handler to retrieve a
259//! connection wrapper for the database:
260//!
261//! ```rust
262//! # #[macro_use] extern crate rocket;
263//! # #[macro_use] extern crate rocket_sync_db_pools;
264//! #
265//! # #[cfg(feature = "diesel_sqlite_pool")]
266//! # mod test {
267//! # use rocket_sync_db_pools::diesel;
268//! #[database("my_db")]
269//! struct MyDatabase(diesel::SqliteConnection);
270//!
271//! #[get("/")]
272//! fn my_handler(conn: MyDatabase) {
273//!     // ...
274//! }
275//! # }
276//! ```
277//!
278//! A connection can be retrieved and used with the `run()` method:
279//!
280//! ```rust
281//! # #[macro_use] extern crate rocket;
282//! # #[macro_use] extern crate rocket_sync_db_pools;
283//! #
284//! # #[cfg(feature = "diesel_sqlite_pool")]
285//! # mod test {
286//! # use rocket_sync_db_pools::diesel;
287//! # type Data = ();
288//! #[database("my_db")]
289//! struct MyDatabase(diesel::SqliteConnection);
290//!
291//! fn load_from_db(conn: &diesel::SqliteConnection) -> Data {
292//!     // Do something with connection, return some data.
293//!     # ()
294//! }
295//!
296//! #[get("/")]
297//! async fn my_handler(mut conn: MyDatabase) -> Data {
298//!     conn.run(|c| load_from_db(c)).await
299//! }
300//! # }
301//! ```
302//!
303//! # Database Support
304//!
305//! Built-in support is provided for many popular databases and drivers. Support
306//! can be easily extended by [`Poolable`] implementations.
307//!
308//! ## Provided
309//!
310//! The list below includes all presently supported database adapters and their
311//! corresponding [`Poolable`] type.
312//!
313// Note: Keep this table in sync with site/guide/6-state.md
314//! | Kind     | Driver                | Version   | `Poolable` Type                | Feature                |
315//! |----------|-----------------------|-----------|--------------------------------|------------------------|
316//! | Sqlite   | [Diesel]              | `2`       | [`diesel::SqliteConnection`]   | `diesel_sqlite_pool`   |
317//! | Postgres | [Diesel]              | `2`       | [`diesel::PgConnection`]       | `diesel_postgres_pool` |
318//! | MySQL    | [Diesel]              | `2`       | [`diesel::MysqlConnection`]    | `diesel_mysql_pool`    |
319//! | Postgres | [Rust-Postgres]       | `0.19`    | [`postgres::Client`]           | `postgres_pool`        |
320//! | Sqlite   | [`Rusqlite`]          | `0.27`    | [`rusqlite::Connection`]       | `sqlite_pool`          |
321//! | Memcache | [`memcache`]          | `0.15`    | [`memcache::Client`]           | `memcache_pool`        |
322//!
323//! [Diesel]: https://diesel.rs
324//! [`diesel::SqliteConnection`]: https://docs.rs/diesel/2/diesel/sqlite/struct.SqliteConnection.html
325//! [`diesel::PgConnection`]: https://docs.rs/diesel/2/diesel/pg/struct.PgConnection.html
326//! [`diesel::MysqlConnection`]: https://docs.rs/diesel/2/diesel/mysql/struct.MysqlConnection.html
327//! [Rust-Postgres]: https://github.com/sfackler/rust-postgres
328//! [`postgres::Client`]: https://docs.rs/postgres/0.19/postgres/struct.Client.html
329//! [`Rusqlite`]: https://github.com/jgallagher/rusqlite
330//! [`rusqlite::Connection`]: https://docs.rs/rusqlite/0.27/rusqlite/struct.Connection.html
331//! [`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
332//! [`memcache`]: https://github.com/aisk/rust-memcache
333//! [`memcache::Client`]: https://docs.rs/memcache/0.15/memcache/struct.Client.html
334//!
335//! The above table lists all the supported database adapters in this library.
336//! In order to use particular `Poolable` type that's included in this library,
337//! you must first enable the feature listed in the "Feature" column. The
338//! interior type of your decorated database type should match the type in the
339//! "`Poolable` Type" column.
340//!
341//! ## Extending
342//!
343//! Extending Rocket's support to your own custom database adapter (or other
344//! database-like struct that can be pooled by `r2d2`) is as easy as
345//! implementing the [`Poolable`] trait. See the documentation for [`Poolable`]
346//! for more details on how to implement it.
347//!
348//! [`FromRequest`]: rocket::request::FromRequest
349//! [request guards]: rocket::request::FromRequest
350//! [`Poolable`]: crate::Poolable
351
352#![doc(html_root_url = "https://api.rocket.rs/v0.5/rocket_sync_db_pools")]
353#![doc(html_favicon_url = "https://rocket.rs/images/favicon.ico")]
354#![doc(html_logo_url = "https://rocket.rs/images/logo-boxed.png")]
355#![cfg_attr(nightly, feature(doc_cfg))]
356
357#[doc(hidden)]
358#[macro_use]
359pub extern crate rocket;
360
361#[cfg(any(
362    feature = "diesel_sqlite_pool",
363    feature = "diesel_postgres_pool",
364    feature = "diesel_mysql_pool"
365))]
366pub use diesel;
367
368#[cfg(feature = "postgres_pool")] pub use postgres;
369#[cfg(feature = "postgres_pool")] pub use r2d2_postgres;
370
371#[cfg(feature = "sqlite_pool")] pub use rusqlite;
372#[cfg(feature = "sqlite_pool")] pub use r2d2_sqlite;
373
374#[cfg(feature = "memcache_pool")] pub use memcache;
375#[cfg(feature = "memcache_pool")] pub use r2d2_memcache;
376
377pub use r2d2;
378
379mod poolable;
380mod config;
381mod error;
382mod connection;
383
384pub use self::poolable::{Poolable, PoolResult};
385pub use self::config::Config;
386pub use self::error::Error;
387
388pub use rocket_sync_db_pools_codegen::*;
389pub use self::connection::*;
390
391/// Example of code generated by the `#[database]` attribute.
392#[cfg(all(nightly, doc, feature = "diesel_sqlite_pool"))]
393pub mod example {
394    use crate::diesel;
395
396    /// Example of code generated by the `#[database]` attribute.
397    ///
398    /// This implementation of `ExampleDb` was generated by:
399    ///
400    /// ```rust
401    /// use rocket_sync_db_pools::{database, diesel};
402    ///
403    /// #[database("example")]
404    /// pub struct ExampleDb(diesel::SqliteConnection);
405    /// ```
406    pub struct ExampleDb(crate::Connection<Self, diesel::SqliteConnection>);
407
408    impl ExampleDb {
409        /// Returns a fairing that initializes the database connection pool
410        /// associated with `Self`.
411        ///
412        /// The fairing _must_ be attached before `Self` can be used as a
413        /// request guard.
414        ///
415        /// # Example
416        ///
417        /// ```rust
418        /// # #[macro_use] extern crate rocket;
419        /// # #[macro_use] extern crate rocket_sync_db_pools;
420        /// #
421        /// # #[cfg(feature = "diesel_sqlite_pool")] {
422        /// use rocket_sync_db_pools::diesel;
423        ///
424        /// #[database("my_db")]
425        /// struct MyConn(diesel::SqliteConnection);
426        ///
427        /// #[launch]
428        /// fn rocket() -> _ {
429        ///     rocket::build().attach(MyConn::fairing())
430        /// }
431        /// # }
432        /// ```
433        pub fn fairing() -> impl crate::rocket::fairing::Fairing {
434            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::fairing(
435                "'example' Database Pool",
436                "example",
437            )
438        }
439
440        /// Returns an opaque type that represents the connection pool backing
441        /// connections of type `Self` _as long as_ the fairing returned by
442        /// [`Self::fairing()`] is attached and has run on `__rocket`.
443        ///
444        /// The returned pool is `Clone`. Values of type `Self` can be retrieved
445        /// from the pool by calling `pool.get().await` which has the same
446        /// signature and semantics as [`Self::get_one()`].
447        ///
448        /// # Example
449        ///
450        /// ```rust
451        /// # #[macro_use] extern crate rocket;
452        /// # #[macro_use] extern crate rocket_sync_db_pools;
453        /// #
454        /// # #[cfg(feature = "diesel_sqlite_pool")] {
455        /// use rocket::tokio::{task, time};
456        /// use rocket::fairing::AdHoc;
457        /// use rocket_sync_db_pools::diesel;
458        ///
459        /// #[database("my_db")]
460        /// struct MyConn(diesel::SqliteConnection);
461        ///
462        /// #[launch]
463        /// fn rocket() -> _ {
464        ///     rocket::build()
465        ///         .attach(MyConn::fairing())
466        ///         .attach(AdHoc::try_on_ignite("Background DB", |rocket| async {
467        ///             let pool = match MyConn::pool(&rocket) {
468        ///                 Some(pool) => pool.clone(),
469        ///                 None => return Err(rocket)
470        ///             };
471        ///
472        ///             // Start a background task that runs some database
473        ///             // operation every 10 seconds. If a connection isn't
474        ///             // available, retries 10 + timeout seconds later.
475        ///             tokio::task::spawn(async move {
476        ///                 loop {
477        ///                     time::sleep(time::Duration::from_secs(10)).await;
478        ///                     if let Some(conn) = pool.get().await {
479        ///                         conn.run(|c| { /* perform db ops */ }).await;
480        ///                     }
481        ///                 }
482        ///             });
483        ///
484        ///             Ok(rocket)
485        ///         }))
486        /// }
487        /// # }
488        /// ```
489        pub fn pool<P: crate::rocket::Phase>(
490            __rocket: &crate::rocket::Rocket<P>,
491        ) -> Option<&crate::ConnectionPool<Self, diesel::SqliteConnection>>
492        {
493            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::pool(
494                &__rocket,
495            )
496        }
497
498        /// Runs the provided function `__f` in an async-safe blocking thread.
499        /// The function is supplied with a mutable reference to the raw
500        /// connection (a value of type `&mut Self.0`). `.await`ing the return
501        /// value of this function yields the value returned by `__f`.
502        ///
503        /// # Example
504        ///
505        /// ```rust
506        /// # #[macro_use] extern crate rocket;
507        /// # #[macro_use] extern crate rocket_sync_db_pools;
508        /// #
509        /// # #[cfg(feature = "diesel_sqlite_pool")] {
510        /// use rocket_sync_db_pools::diesel;
511        ///
512        /// #[database("my_db")]
513        /// struct MyConn(diesel::SqliteConnection);
514        ///
515        /// #[get("/")]
516        /// async fn f(conn: MyConn) {
517        ///     // The type annotation is illustrative and isn't required.
518        ///     let result = conn.run(|c: &mut diesel::SqliteConnection| {
519        ///         // Use `c`.
520        ///     }).await;
521        /// }
522        /// # }
523        /// ```
524        pub async fn run<F, R>(&self, __f: F) -> R
525        where
526            F: FnOnce(&mut diesel::SqliteConnection) -> R + Send + 'static,
527            R: Send + 'static,
528        {
529            self.0.run(__f).await
530        }
531
532        /// Retrieves a connection of type `Self` from the `rocket` instance.
533        /// Returns `Some` as long as `Self::fairing()` has been attached and
534        /// there is a connection available within at most `timeout` seconds.
535        pub async fn get_one<P: crate::rocket::Phase>(
536            __rocket: &crate::rocket::Rocket<P>,
537        ) -> Option<Self> {
538            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::get_one(
539                &__rocket,
540            )
541            .await
542            .map(Self)
543        }
544    }
545
546    /// Retrieves a connection from the database pool or fails with a
547    /// `Status::ServiceUnavailable` if doing so times out.
548    impl<'r> crate::rocket::request::FromRequest<'r> for ExampleDb {
549        type Error = ();
550        #[allow(
551            clippy::let_unit_value,
552            clippy::no_effect_underscore_binding,
553            clippy::shadow_same,
554            clippy::type_complexity,
555            clippy::type_repetition_in_bounds,
556            clippy::used_underscore_binding
557        )]
558        fn from_request<'life0, 'async_trait>(
559            __r: &'r crate::rocket::request::Request<'life0>,
560        ) -> ::core::pin::Pin<
561            Box<
562                dyn ::core::future::Future<
563                        Output = crate::rocket::request::Outcome<Self, ()>,
564                    > + ::core::marker::Send
565                    + 'async_trait,
566            >,
567        >
568        where
569            'r: 'async_trait,
570            'life0: 'async_trait,
571            Self: 'async_trait,
572        {
573            Box::pin(async move {
574                if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<
575                    crate::rocket::request::Outcome<Self, ()>,
576                > {
577                    return __ret;
578                }
579                let __r = __r;
580                let __ret: crate::rocket::request::Outcome<Self, ()> = {
581                    < crate :: Connection < Self , diesel :: SqliteConnection > >
582                        :: from_request (__r) . await . map (Self)
583                };
584                #[allow(unreachable_code)]
585                __ret
586            })
587        }
588    }
589    impl crate::rocket::Sentinel for ExampleDb {
590        fn abort(
591            __r: &crate::rocket::Rocket<crate::rocket::Ignite>,
592        ) -> bool {
593            <crate::Connection<Self, diesel::SqliteConnection>>::abort(__r)
594        }
595    }
596}