mirror of https://github.com/rwf2/Rocket.git
Clean up connection pooling documentation.
This commit is contained in:
parent
60b9f06407
commit
fe9fad339e
|
@ -11,7 +11,7 @@
|
||||||
target
|
target
|
||||||
|
|
||||||
# Generated databases
|
# Generated databases
|
||||||
db.sql
|
db.sqlite
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
|
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
|
||||||
|
|
|
@ -19,7 +19,9 @@ database_attribute = []
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies.derive_utils]
|
||||||
|
git = "https://github.com/SergioBenitez/derive-utils"
|
||||||
|
rev = "160da392"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "0.6"
|
quote = "0.6"
|
||||||
proc-macro2 = { version = "0.4", features = ["nightly"] }
|
|
||||||
syn = { version = "0.14", features = ["full", "extra-traits"] }
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use proc_macro::{TokenStream, Diagnostic};
|
use proc_macro::TokenStream;
|
||||||
|
use derive_utils::{Spanned, Result};
|
||||||
use syn::{DataStruct, Fields, Data, Type, LitStr, DeriveInput, Ident, Visibility};
|
use syn::{DataStruct, Fields, Data, Type, LitStr, DeriveInput, Ident, Visibility};
|
||||||
use spanned::Spanned;
|
|
||||||
|
|
||||||
type Result<T> = ::std::result::Result<T, Diagnostic>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DatabaseInvocation {
|
struct DatabaseInvocation {
|
||||||
|
@ -20,10 +18,10 @@ struct DatabaseInvocation {
|
||||||
|
|
||||||
const EXAMPLE: &str = "example: `struct MyDatabase(diesel::SqliteConnection);`";
|
const EXAMPLE: &str = "example: `struct MyDatabase(diesel::SqliteConnection);`";
|
||||||
const ONLY_ON_STRUCTS_MSG: &str = "`database` attribute can only be used on structs";
|
const ONLY_ON_STRUCTS_MSG: &str = "`database` attribute can only be used on structs";
|
||||||
const ONLY_UNNAMED_FIELDS: &str = "`database` attribute can only be applied to structs with \
|
const ONLY_UNNAMED_FIELDS: &str = "`database` attribute can only be applied to \
|
||||||
exactly one unnamed field";
|
structs with exactly one unnamed field";
|
||||||
const NO_GENERIC_STRUCTS: &str = "`database` attribute cannot be applied to a struct with a \
|
const NO_GENERIC_STRUCTS: &str = "`database` attribute cannot be applied to a struct \
|
||||||
generic type";
|
with a generic type";
|
||||||
|
|
||||||
fn parse_invocation(attr: TokenStream, input: TokenStream) -> Result<DatabaseInvocation> {
|
fn parse_invocation(attr: TokenStream, input: TokenStream) -> Result<DatabaseInvocation> {
|
||||||
let attr_stream2 = ::proc_macro2::TokenStream::from(attr);
|
let attr_stream2 = ::proc_macro2::TokenStream::from(attr);
|
||||||
|
@ -61,44 +59,61 @@ fn parse_invocation(attr: TokenStream, input: TokenStream) -> Result<DatabaseInv
|
||||||
pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
|
pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
|
||||||
let invocation = parse_invocation(attr, input)?;
|
let invocation = parse_invocation(attr, input)?;
|
||||||
|
|
||||||
|
// Store everything we're going to need to generate code.
|
||||||
let connection_type = &invocation.connection_type;
|
let connection_type = &invocation.connection_type;
|
||||||
let database_name = &invocation.db_name;
|
let name = &invocation.db_name;
|
||||||
let request_guard_type = &invocation.type_name;
|
let request_guard_type = &invocation.type_name;
|
||||||
let request_guard_vis = &invocation.visibility;
|
let vis = &invocation.visibility;
|
||||||
let pool_type = Ident::new(&format!("{}Pool", request_guard_type), request_guard_type.span());
|
let pool_type = Ident::new(&format!("{}Pool", request_guard_type), request_guard_type.span());
|
||||||
|
let fairing_name = format!("'{}' Database Pool", name);
|
||||||
|
|
||||||
let tokens = quote! {
|
// A few useful paths.
|
||||||
#request_guard_vis struct #request_guard_type(
|
let databases = quote!(::rocket_contrib::databases);
|
||||||
pub ::rocket_contrib::databases::r2d2::PooledConnection<<#connection_type as ::rocket_contrib::databases::Poolable>::Manager>
|
let r2d2 = quote!(#databases::r2d2);
|
||||||
|
let request = quote!(::rocket::request);
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
/// The request guard type.
|
||||||
|
#vis struct #request_guard_type(
|
||||||
|
pub #r2d2::PooledConnection<<#connection_type as #databases::Poolable>::Manager>
|
||||||
);
|
);
|
||||||
#request_guard_vis struct #pool_type(
|
|
||||||
::rocket_contrib::databases::r2d2::Pool<<#connection_type as ::rocket_contrib::databases::Poolable>::Manager>
|
/// The pool type.
|
||||||
|
#vis struct #pool_type(
|
||||||
|
#r2d2::Pool<<#connection_type as #databases::Poolable>::Manager>
|
||||||
);
|
);
|
||||||
|
|
||||||
impl #request_guard_type {
|
impl #request_guard_type {
|
||||||
|
/// Returns a fairing that initializes the associated database
|
||||||
|
/// connection pool.
|
||||||
pub fn fairing() -> impl ::rocket::fairing::Fairing {
|
pub fn fairing() -> impl ::rocket::fairing::Fairing {
|
||||||
use ::rocket_contrib::databases::Poolable;
|
use #databases::Poolable;
|
||||||
|
|
||||||
::rocket::fairing::AdHoc::on_attach(|rocket| {
|
::rocket::fairing::AdHoc::on_attach(#fairing_name, |rocket| {
|
||||||
let pool = ::rocket_contrib::databases::database_config(#database_name, rocket.config())
|
let pool = #databases::database_config(#name, rocket.config())
|
||||||
.map(#connection_type::pool);
|
.map(#connection_type::pool);
|
||||||
|
|
||||||
match pool {
|
match pool {
|
||||||
Ok(Ok(p)) => Ok(rocket.manage(#pool_type(p))),
|
Ok(Ok(p)) => Ok(rocket.manage(#pool_type(p))),
|
||||||
Err(config_error) => {
|
Err(config_error) => {
|
||||||
::rocket::logger::log_err(&format!("Error while instantiating database: '{}': {}", #database_name, config_error));
|
::rocket::logger::log_err(false,
|
||||||
|
&format!("Database configuration failure: '{}'", #name));
|
||||||
|
::rocket::logger::log_err(true, &format!("{}", config_error));
|
||||||
Err(rocket)
|
Err(rocket)
|
||||||
},
|
},
|
||||||
Ok(Err(pool_error)) => {
|
Ok(Err(pool_error)) => {
|
||||||
::rocket::logger::log_err(&format!("Error initializing pool for '{}': {:?}", #database_name, pool_error));
|
::rocket::logger::log_err(false,
|
||||||
|
&format!("Failed to initialize pool for '{}'", #name));
|
||||||
|
::rocket::logger::log_err(true, &format!("{:?}", pool_error));
|
||||||
Err(rocket)
|
Err(rocket)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a connection of type `Self` from the `rocket` instance. Returns `Some` as long as
|
/// Retrieves a connection of type `Self` from the `rocket`
|
||||||
/// `Self::fairing()` has been attached and there is at least one connection in the pool.
|
/// instance. Returns `Some` as long as `Self::fairing()` has been
|
||||||
|
/// attached and there is at least one connection in the pool.
|
||||||
pub fn get_one(rocket: &::rocket::Rocket) -> Option<Self> {
|
pub fn get_one(rocket: &::rocket::Rocket) -> Option<Self> {
|
||||||
rocket.state::<#pool_type>()
|
rocket.state::<#pool_type>()
|
||||||
.and_then(|pool| pool.0.get().ok())
|
.and_then(|pool| pool.0.get().ok())
|
||||||
|
@ -115,19 +130,18 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStrea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'r> ::rocket::request::FromRequest<'a, 'r> for #request_guard_type {
|
impl<'a, 'r> #request::FromRequest<'a, 'r> for #request_guard_type {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn from_request(request: &'a ::rocket::request::Request<'r>) -> ::rocket::request::Outcome<Self, Self::Error> {
|
fn from_request(request: &'a #request::Request<'r>) -> #request::Outcome<Self, ()> {
|
||||||
|
use ::rocket::{Outcome, http::Status};
|
||||||
let pool = request.guard::<::rocket::State<#pool_type>>()?;
|
let pool = request.guard::<::rocket::State<#pool_type>>()?;
|
||||||
|
|
||||||
match pool.0.get() {
|
match pool.0.get() {
|
||||||
Ok(conn) => ::rocket::Outcome::Success(#request_guard_type(conn)),
|
Ok(conn) => Outcome::Success(#request_guard_type(conn)),
|
||||||
Err(_) => ::rocket::Outcome::Failure((::rocket::http::Status::ServiceUnavailable, ())),
|
Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}.into())
|
||||||
|
|
||||||
Ok(tokens.into())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#![feature(proc_macro_span, proc_macro_diagnostic)]
|
#![feature(proc_macro_span, proc_macro_diagnostic)]
|
||||||
|
#![feature(crate_visibility_modifier)]
|
||||||
#![recursion_limit="256"]
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
//! # Rocket Contrib - Code Generation
|
//! # Rocket Contrib - Code Generation
|
||||||
|
@ -23,22 +24,24 @@
|
||||||
//! DATABASE_NAME := (string literal)
|
//! DATABASE_NAME := (string literal)
|
||||||
//! </pre>
|
//! </pre>
|
||||||
|
|
||||||
extern crate syn;
|
extern crate derive_utils;
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
#[macro_use] extern crate quote;
|
#[macro_use] extern crate quote;
|
||||||
|
|
||||||
mod spanned;
|
#[allow(unused_imports)]
|
||||||
|
crate use derive_utils::{syn, proc_macro2};
|
||||||
|
|
||||||
#[cfg(feature = "database_attribute")]
|
#[cfg(feature = "database_attribute")]
|
||||||
mod database;
|
mod database;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(unused_imports)]
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
/// The procedural macro for the `databases` annotation.
|
||||||
#[cfg(feature = "database_attribute")]
|
#[cfg(feature = "database_attribute")]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
/// The procedural macro for the `databases` annotation.
|
|
||||||
pub fn database(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn database(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
::database::database_attr(attr, input).unwrap_or_else(|diag| {
|
::database::database_attr(attr, input).unwrap_or_else(|diag| {
|
||||||
diag.emit();
|
diag.emit();
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
use proc_macro::Span;
|
|
||||||
|
|
||||||
use quote::ToTokens;
|
|
||||||
|
|
||||||
pub trait Spanned {
|
|
||||||
fn span(&self) -> Span;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Remove this once proc_macro's stabilize.
|
|
||||||
impl<T: ToTokens> Spanned for T {
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
let token_stream = self.into_token_stream();
|
|
||||||
let mut iter = token_stream.into_iter();
|
|
||||||
let mut span = match iter.next() {
|
|
||||||
Some(tt) => tt.span().unstable(),
|
|
||||||
None => {
|
|
||||||
return Span::call_site();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for tt in iter {
|
|
||||||
if let Some(joined) = span.join(tt.span().unstable()) {
|
|
||||||
span = joined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,8 +17,12 @@ msgpack = ["serde", "rmp-serde"]
|
||||||
tera_templates = ["tera", "templates"]
|
tera_templates = ["tera", "templates"]
|
||||||
handlebars_templates = ["handlebars", "templates"]
|
handlebars_templates = ["handlebars", "templates"]
|
||||||
static_files = []
|
static_files = []
|
||||||
|
|
||||||
|
# Database pooling features.
|
||||||
|
# Iternal use only.
|
||||||
database_pool_codegen = ["rocket_contrib_codegen", "rocket_contrib_codegen/database_attribute"]
|
database_pool_codegen = ["rocket_contrib_codegen", "rocket_contrib_codegen/database_attribute"]
|
||||||
database_pool = ["r2d2", "database_pool_codegen"]
|
database_pool = ["r2d2", "database_pool_codegen"]
|
||||||
|
# External features.
|
||||||
diesel_pg_pool = ["database_pool", "diesel/postgres", "diesel/r2d2"]
|
diesel_pg_pool = ["database_pool", "diesel/postgres", "diesel/r2d2"]
|
||||||
diesel_sqlite_pool = ["database_pool", "diesel/sqlite", "diesel/r2d2"]
|
diesel_sqlite_pool = ["database_pool", "diesel/sqlite", "diesel/r2d2"]
|
||||||
diesel_mysql_pool = ["database_pool", "diesel/mysql", "diesel/r2d2"]
|
diesel_mysql_pool = ["database_pool", "diesel/mysql", "diesel/r2d2"]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Traits, utilities, and a macro for easy database connection pooling.
|
||||||
|
//!
|
||||||
//! # Overview
|
//! # Overview
|
||||||
//!
|
//!
|
||||||
//! This module provides traits, utilities, and a procedural macro that allows
|
//! This module provides traits, utilities, and a procedural macro that allows
|
||||||
|
@ -5,8 +7,7 @@
|
||||||
//! connection pools. A _database connection pool_ is a data structure that
|
//! connection pools. A _database connection pool_ is a data structure that
|
||||||
//! maintains active database connections for later use in the application.
|
//! maintains active database connections for later use in the application.
|
||||||
//! This implementation of connection pooling support is based on
|
//! This implementation of connection pooling support is based on
|
||||||
//! [`r2d2`](https://crates.io/crates/r2d2) and exposes connections through
|
//! [`r2d2`] and exposes connections through [request guards]. Databases are
|
||||||
//! [request guards](../../rocket/request/trait.FromRequest.html). Databases are
|
|
||||||
//! individually configured through Rocket's regular configuration mechanisms: a
|
//! individually configured through Rocket's regular configuration mechanisms: a
|
||||||
//! `Rocket.toml` file, environment variables, or procedurally.
|
//! `Rocket.toml` file, environment variables, or procedurally.
|
||||||
//!
|
//!
|
||||||
|
@ -20,26 +21,28 @@
|
||||||
//! 3. Use the request guard to retrieve a connection in a handler.
|
//! 3. Use the request guard to retrieve a connection in a handler.
|
||||||
//! (see [Handlers](#handlers))
|
//! (see [Handlers](#handlers))
|
||||||
//!
|
//!
|
||||||
//! For a list of supported databases, see [Provided Databases](#provided).
|
//! For a list of supported databases, see [Provided Databases](#provided). This
|
||||||
//! This support can be easily extended by implementing the
|
//! support can be easily extended by implementing the [`Poolable`] trait. See
|
||||||
//! [`Poolable`](trait.Poolable.html) trait. See [Extending](#extending)
|
//! [Extending](#extending) for more.
|
||||||
//! for more.
|
|
||||||
//!
|
//!
|
||||||
//! The next section provides a complete but un-detailed example of these steps
|
//! [`r2d2`]: https://crates.io/crates/r2d2
|
||||||
//! in actions. The sections following provide more detail for each component.
|
//! [request guards]: [rocket::FromRequest]
|
||||||
//!
|
//!
|
||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! Before using this library, the `database_pool` feature in `rocket_contrib`
|
//! Before using this library, the feature corresponding to your database type
|
||||||
//! must be enabled:
|
//! in `rocket_contrib` must be enabled:
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies.rocket_contrib]
|
//! [dependencies.rocket_contrib]
|
||||||
//! version = "0.4.0-dev"
|
//! version = "0.4.0-dev"
|
||||||
//! default-features = false
|
//! default-features = false
|
||||||
//! features = ["database_pool", "diesel_sqlite_pool"]
|
//! features = ["diesel_sqlite_pool"]
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! See [Provided](#provided) for a list of supported database and their
|
||||||
|
//! associated feature name.
|
||||||
|
//!
|
||||||
//! In `Rocket.toml` or the equivalent via environment variables:
|
//! In `Rocket.toml` or the equivalent via environment variables:
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
|
@ -49,29 +52,49 @@
|
||||||
//!
|
//!
|
||||||
//! In your application's source code, one-time:
|
//! In your application's source code, one-time:
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust
|
||||||
//! #![feature(use_extern_macros)]
|
//! # #![feature(use_extern_macros)]
|
||||||
//! extern crate rocket;
|
//! #
|
||||||
//! extern crate rocket_contrib;
|
//! # extern crate rocket;
|
||||||
//!
|
//! # extern crate rocket_contrib;
|
||||||
|
//! #
|
||||||
//! use rocket_contrib::databases::{database, diesel};
|
//! use rocket_contrib::databases::{database, diesel};
|
||||||
//!
|
//!
|
||||||
//! #[database("sqlite_logs")]
|
//! #[database("sqlite_logs")]
|
||||||
//! struct LogsDbConn(diesel::SqliteConnection);
|
//! struct LogsDbConn(diesel::SqliteConnection);
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
|
//! # if false {
|
||||||
//! rocket::ignite()
|
//! rocket::ignite()
|
||||||
//! .attach(LogsDbConn::fairing())
|
//! .attach(LogsDbConn::fairing())
|
||||||
//! .launch();
|
//! .launch();
|
||||||
|
//! # }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Whenever a connection to the database is needed:
|
//! Whenever a connection to the database is needed:
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust
|
||||||
|
//! # #![feature(plugin, decl_macro, use_extern_macros)]
|
||||||
|
//! # #![plugin(rocket_codegen)]
|
||||||
|
//! #
|
||||||
|
//! # extern crate rocket;
|
||||||
|
//! # extern crate rocket_contrib;
|
||||||
|
//! #
|
||||||
|
//! # use rocket_contrib::databases::{database, diesel};
|
||||||
|
//! #
|
||||||
|
//! # #[database("sqlite_logs")]
|
||||||
|
//! # struct LogsDbConn(diesel::SqliteConnection);
|
||||||
|
//! #
|
||||||
|
//! # type Logs = ();
|
||||||
|
//! # type Result<T> = ::std::result::Result<T, ()>;
|
||||||
|
//! #
|
||||||
//! #[get("/logs/<id>")]
|
//! #[get("/logs/<id>")]
|
||||||
//! fn get_logs(conn: LogsDbConn, id: LogId) -> Result<Logs> {
|
//! fn get_logs(conn: LogsDbConn, id: usize) -> Result<Logs> {
|
||||||
|
//! # /*
|
||||||
//! Logs::by_id(&conn, id)
|
//! Logs::by_id(&conn, id)
|
||||||
|
//! # */
|
||||||
|
//! # Ok(())
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -79,38 +102,49 @@
|
||||||
//!
|
//!
|
||||||
//! ## Configuration
|
//! ## Configuration
|
||||||
//!
|
//!
|
||||||
//! There are a few ways to configure your database connection. You can use the
|
//! Databases can be configured via various mechanisms: `Rocket.toml`,
|
||||||
//! `Rocket.toml` file, you can build it yourself procedurally via the
|
//! procedurally via `rocket::custom()`, or via environment variables.
|
||||||
//! `rocket::custom()` method, or through environment variables.
|
|
||||||
//!
|
//!
|
||||||
//! ### Configuring via `Rocket.toml`
|
//! ### `Rocket.toml`
|
||||||
//!
|
//!
|
||||||
//! The following examples are all valid ways of configuring your database via
|
//! To configure a database via `Rocket.toml`, add a table for each database
|
||||||
//! the `Rocket.toml` file.
|
//! to the `databases` table where the key is a name of your choice. The table
|
||||||
//!
|
//! should have a `url` key and, optionally, a `pool_size` key. This looks as
|
||||||
//! The basic structure includes attaching a key to the `global.databases` table
|
//! follows:
|
||||||
//! and including the __required__ keys `url` and `pool_size`. Additional
|
|
||||||
//! options that can be added to the table vary by adapter and are referenced
|
|
||||||
//! below in the [Supported Databases](#provided) section.
|
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
|
//! // Option 1:
|
||||||
//! [global.databases]
|
//! [global.databases]
|
||||||
//! my_database = { url = "database.sqlite", pool_size = 10 }
|
//! sqlite_db = { url = "db.sqlite" }
|
||||||
//!
|
//!
|
||||||
//! [[global.databases.other_database]]
|
//! // Option 2:
|
||||||
//! url = "mysql://root:root@localhost/other_database
|
//! [global.databases.pg_db]
|
||||||
//! pool_size = 25
|
//! url = "mysql://root:root@localhost/pg_db"
|
||||||
|
//!
|
||||||
|
//! // With a `pool_size` key:
|
||||||
|
//! [global.databases]
|
||||||
|
//! sqlite_db = { url = "db.sqlite", pool_size = 20 }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Configuring procedurally
|
//! The table _requires_ one key:
|
||||||
//!
|
//!
|
||||||
//! It's also possible to procedurally configure your database via the
|
//! * `url` - the URl to the database
|
||||||
//! `rocket::custom()` method. Below is an example of doing this:
|
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! Additionally, all configurations accept the following _optional_ keys:
|
||||||
|
//!
|
||||||
|
//! * `pool_size` - the size of the pool, i.e., the number of connections to
|
||||||
|
//! pool (defaults to the configured number of workers)
|
||||||
|
//!
|
||||||
|
//! Additional options may be required or supported by other adapters.
|
||||||
|
//!
|
||||||
|
//! ### Procedurally
|
||||||
|
//!
|
||||||
|
//! Databases can also be configured procedurally database via
|
||||||
|
//! `rocket::custom()`. The example below does just this:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
//! extern crate rocket;
|
//! extern crate rocket;
|
||||||
//!
|
//!
|
||||||
//! use std::io::Error;
|
|
||||||
//! use std::collections::HashMap;
|
//! use std::collections::HashMap;
|
||||||
//! use rocket::config::{Config, Environment, Value};
|
//! use rocket::config::{Config, Environment, Value};
|
||||||
//!
|
//!
|
||||||
|
@ -118,6 +152,8 @@
|
||||||
//! let mut database_config = HashMap::new();
|
//! let mut database_config = HashMap::new();
|
||||||
//! let mut databases = HashMap::new();
|
//! let mut databases = HashMap::new();
|
||||||
//!
|
//!
|
||||||
|
//! // This is the same as the following TOML:
|
||||||
|
//! // my_db = { url = "database.sqlite" }
|
||||||
//! database_config.insert("url", Value::from("database.sqlite"));
|
//! database_config.insert("url", Value::from("database.sqlite"));
|
||||||
//! databases.insert("my_db", Value::from(database_config));
|
//! databases.insert("my_db", Value::from(database_config));
|
||||||
//!
|
//!
|
||||||
|
@ -126,15 +162,18 @@
|
||||||
//! .finalize()
|
//! .finalize()
|
||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//!
|
//!
|
||||||
|
//! # if false {
|
||||||
//! rocket::custom(config).launch();
|
//! rocket::custom(config).launch();
|
||||||
|
//! # }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Configuring via Environment Variable
|
//! ### Environment Variables
|
||||||
//!
|
//!
|
||||||
//! The final way to configure your databases is via an environment variable.
|
//! Lastly, databases can be configured via environment variables by specifying
|
||||||
//! Following the syntax laid out in the guide on [Environment Variables](https://rocket.rs/guide/configuration/#environment-variables),
|
//! the `databases` table as detailed in the [Environment Variables
|
||||||
//! you can configure your database this way. Below is an example
|
//! configuration
|
||||||
|
//! guide](https://rocket.rs/guide/configuration/#environment-variables):
|
||||||
//!
|
//!
|
||||||
//! ```bash
|
//! ```bash
|
||||||
//! ROCKET_DATABASES={my_db={url="db.sqlite"}}
|
//! ROCKET_DATABASES={my_db={url="db.sqlite"}}
|
||||||
|
@ -142,49 +181,61 @@
|
||||||
//!
|
//!
|
||||||
//! ## Guard Types
|
//! ## Guard Types
|
||||||
//!
|
//!
|
||||||
//! The included database support generates request guard types that can be used
|
//! Once a database has been configured, the `#[database]` attribute can be used
|
||||||
//! with Rocket handlers. In order to associate a configured database with a
|
//! to tie a type in your application to a configured database. The database
|
||||||
//! type, you need to use the `database` procedural macro:
|
//! attributes accepts a single string parameter that indicates the name of the
|
||||||
|
//! database. This corresponds to the database name set as the database's
|
||||||
|
//! configuration key.
|
||||||
|
//!
|
||||||
|
//! The attribute can only be applied to unit-like structs with one type. The
|
||||||
|
//! internal type of the structure must implement [`Poolable`].
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # #![feature(use_extern_macros)]
|
//! # #![feature(use_extern_macros)]
|
||||||
//! # extern crate rocket;
|
//! # extern crate rocket;
|
||||||
//! # extern crate rocket_contrib;
|
//! # extern crate rocket_contrib;
|
||||||
//! # use rocket_contrib::databases::{database, diesel};
|
//! use rocket_contrib::databases::{database, diesel};
|
||||||
//!
|
//!
|
||||||
//! #[database("my_db")]
|
//! #[database("my_db")]
|
||||||
//! struct MyDatabase(diesel::SqliteConnection);
|
//! struct MyDatabase(diesel::SqliteConnection);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! From there, the macro will generate code to turn your defined type into a
|
//! The macro generates a [`FromRequest`] implementation for the decorated type,
|
||||||
//! valid request guard type. The interior type must have an implementation of
|
//! allowing the type to be used as a request guard. This implementation
|
||||||
//! the [`Poolable` trait](trait.Poolable.html). The trait implements methods
|
//! retrieves a connection from the database pool or fails with a
|
||||||
//! on the interior type that are used by the generated code to spin up a
|
//! `Status::ServiceUnavailable` if no connections are available. The macro also
|
||||||
//! connection pool. The trait can be used to extend other connection types that
|
//! generates an implementation of the [`Deref`](::std::ops::Deref) trait with
|
||||||
//! aren't supported in this library. See the section on [Extending](#extending)
|
//! the internal `Poolable` type as the target.
|
||||||
//! for more information.
|
|
||||||
//!
|
//!
|
||||||
//! The generated code will give your defined type two methods, `get_one` and
|
//! [`FromRequest`]: /rocket/request/trait.FromRequest.html
|
||||||
//! `fairing`, as well as implementations of the [`FromRequest`](../../rocket/request/trait.FromRequest.html)
|
|
||||||
//! and [`Deref`](../../std/ops/trait.Deref.html) traits.
|
|
||||||
//!
|
//!
|
||||||
//! The `fairing` method will allow you to attach your database type to the
|
//! The macro will also generate two inherent methods on the decorated type:
|
||||||
//! application state via the method call. You __will need__ to call the
|
|
||||||
//! `fairing` method on your type in order to be able to retrieve connections
|
|
||||||
//! in your request guards.
|
|
||||||
//!
|
//!
|
||||||
//! Below is an example:
|
//! * `fn fairing() -> impl Fairing`
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! Returns a fairing that initializes the associated database connection
|
||||||
|
//! pool.
|
||||||
|
//!
|
||||||
|
//! * `fn get_one(&Rocket) -> Option<Self>`
|
||||||
|
//!
|
||||||
|
//! Retrieves a connection from the configured pool. Returns `Some` as long
|
||||||
|
//! as `Self::fairing()` has been attached and there is at least one
|
||||||
|
//! connection in the pool.
|
||||||
|
//!
|
||||||
|
//! The fairing returned from the generated `fairing()` method _must_ be
|
||||||
|
//! attached for the request guard implementation to succeed. Putting the pieces
|
||||||
|
//! together, a use of the `#[database]` attribute looks as follows:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
//! # #![feature(use_extern_macros)]
|
//! # #![feature(use_extern_macros)]
|
||||||
//! #
|
|
||||||
//! # extern crate rocket;
|
//! # extern crate rocket;
|
||||||
//! # extern crate rocket_contrib;
|
//! # extern crate rocket_contrib;
|
||||||
//! #
|
//! #
|
||||||
//! # use std::collections::HashMap;
|
//! # use std::collections::HashMap;
|
||||||
//! # use rocket::config::{Config, Environment, Value};
|
//! # use rocket::config::{Config, Environment, Value};
|
||||||
//! # use rocket_contrib::databases::{database, diesel};
|
|
||||||
//! #
|
//! #
|
||||||
|
//! use rocket_contrib::databases::{database, diesel};
|
||||||
|
//!
|
||||||
//! #[database("my_db")]
|
//! #[database("my_db")]
|
||||||
//! struct MyDatabase(diesel::SqliteConnection);
|
//! struct MyDatabase(diesel::SqliteConnection);
|
||||||
//!
|
//!
|
||||||
|
@ -201,88 +252,116 @@
|
||||||
//! # .finalize()
|
//! # .finalize()
|
||||||
//! # .unwrap();
|
//! # .unwrap();
|
||||||
//! #
|
//! #
|
||||||
|
//! # if false {
|
||||||
//! rocket::custom(config)
|
//! rocket::custom(config)
|
||||||
//! .attach(MyDatabase::fairing()); // Required!
|
//! .attach(MyDatabase::fairing())
|
||||||
//! .launch();
|
//! .launch();
|
||||||
|
//! # }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Handlers
|
//! ## Handlers
|
||||||
//!
|
//!
|
||||||
//! For request handlers, you should use the database type you defined in your
|
//! Finally, simply use your type as a request guard in a handler to retrieve a
|
||||||
//! code as a request guard. Because of the `FromRequest` implementation that's
|
//! connection to a given database:
|
||||||
//! generated at compile-time, you can use this type in such a way. For example:
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #![feature(use_extern_macros)]
|
||||||
|
//! # #![feature(plugin, decl_macro)]
|
||||||
|
//! # #![plugin(rocket_codegen)]
|
||||||
|
//! #
|
||||||
|
//! # extern crate rocket;
|
||||||
|
//! # extern crate rocket_contrib;
|
||||||
|
//! # use rocket_contrib::databases::{database, diesel};
|
||||||
|
//! #[database("my_db")]
|
||||||
|
//! struct MyDatabase(diesel::SqliteConnection);
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
|
||||||
//! #[database("my_db")
|
|
||||||
//! struct MyDatabase(diesel::MysqlConnection);
|
|
||||||
//! ...
|
|
||||||
//! #[get("/")]
|
//! #[get("/")]
|
||||||
//! fn my_handler(conn: MyDatabase) {
|
//! fn my_handler(conn: MyDatabase) {
|
||||||
|
//! # /*
|
||||||
//! ...
|
//! ...
|
||||||
|
//! # */
|
||||||
//! }
|
//! }
|
||||||
|
//! # fn main() { }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Additionally, because of the `Deref` implementation, you can dereference
|
//! The generated `Deref` implementation allows easy access to the inner
|
||||||
//! the database type in order to access the inner connection type. For example:
|
//! connection type:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #![feature(plugin, decl_macro, use_extern_macros)]
|
||||||
|
//! # #![plugin(rocket_codegen)]
|
||||||
|
//! #
|
||||||
|
//! # extern crate rocket;
|
||||||
|
//! # extern crate rocket_contrib;
|
||||||
|
//! # use rocket_contrib::databases::{database, diesel};
|
||||||
|
//! #
|
||||||
|
//! # type Data = ();
|
||||||
|
//! #
|
||||||
|
//! #[database("my_db")]
|
||||||
|
//! struct MyDatabase(diesel::SqliteConnection);
|
||||||
|
//!
|
||||||
|
//! fn load_from_db(conn: &diesel::SqliteConnection) -> Data {
|
||||||
|
//! // Do something with connection, return some data.
|
||||||
|
//! # ()
|
||||||
|
//! }
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
|
||||||
//! #[get("/")]
|
//! #[get("/")]
|
||||||
//! fn my_handler(conn: MyDatabase) {
|
//! fn my_handler(conn: MyDatabase) -> Data {
|
||||||
//! ...
|
//! load_from_db(&conn)
|
||||||
//! Thing::load(&conn);
|
|
||||||
//! ...
|
|
||||||
//! }
|
//! }
|
||||||
|
//! # fn main() { }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Under the hood, the dereferencing of your type is returning the interior
|
|
||||||
//! type of your connection:
|
|
||||||
//!
|
|
||||||
//! ```rust,ignore
|
|
||||||
//! &self.0
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! This section should be simple. It should cover:
|
|
||||||
//!
|
|
||||||
//! * The fact that `MyType` is not a request guard, and you can use it.
|
|
||||||
//! * The `Deref` impl and what it means for using `&my_conn`.
|
|
||||||
//!
|
|
||||||
//! # Database Support
|
//! # Database Support
|
||||||
//!
|
//!
|
||||||
//! This library provides built-in support for many popular databases and their
|
//! Built-in support is provided for many popular databases and drivers. Support
|
||||||
//! corresponding drivers. It also makes extending this support simple.
|
//! can be easily extended by [`Poolable`] implementations.
|
||||||
//!
|
//!
|
||||||
//! ## Provided
|
//! ## Provided
|
||||||
//!
|
//!
|
||||||
//! The list below includes all presently supported database adapters, their
|
//! The list below includes all presently supported database adapters and their
|
||||||
//! corresponding [`Poolable`] type, and any special considerations for
|
//! corresponding [`Poolable`] type.
|
||||||
//! configuration, if any.
|
|
||||||
//!
|
//!
|
||||||
//! | Database Kind | Driver | `Poolable` Type | Feature | Notes |
|
//! | Kind | Driver | [`Poolable`] Type | Feature |
|
||||||
//! | -- ------------- | ----------------------- | ------------------------- | --------------------- | ----- |
|
//! |----------|-----------------------|--------------------------------|------------------------|
|
||||||
//! | MySQL | [Diesel](https://diesel.rs) | [`diesel::MysqlConnection`](http://docs.diesel.rs/diesel/mysql/struct.MysqlConnection.html) | `diesel_mysql_pool` | None |
|
//! | MySQL | [Diesel] | [`diesel::MysqlConnection`] | `diesel_mysql_pool` |
|
||||||
//! | MySQL | [`rust-mysql-simple`](https://github.com/blackbeam/rust-mysql-simple) | [`mysql::conn`](https://docs.rs/mysql/14.0.0/mysql/struct.Conn.html) | `mysql_pool` | None |
|
//! | MySQL | [`rust-mysql-simple`] | [`mysql::conn`] | `mysql_pool` |
|
||||||
//! | Postgres | [Diesel](https://diesel.rs) | [`diesel::PgConnection`](http://docs.diesel.rs/diesel/pg/struct.PgConnection.html) | `diesel_postgres_pool` | None |
|
//! | Postgres | [Diesel] | [`diesel::PgConnection`] | `diesel_postgres_pool` |
|
||||||
//! | Postgres | [Rust-Postgres](https://github.com/sfackler/rust-postgres) | [`postgres::Connection`](https://docs.rs/postgres/0.15.2/postgres/struct.Connection.html) | `postgres_pool` | None |
|
//! | Postgres | [Rust-Postgres] | [`postgres::Connection`] | `postgres_pool` |
|
||||||
//! | Sqlite | [Diesel](https://diesel.rs) | [`diesel::SqliteConnection`](http://docs.diesel.rs/diesel/prelude/struct.SqliteConnection.html) | `diesel_sqlite_pool` | None |
|
//! | Sqlite | [Diesel] | [`diesel::SqliteConnection`] | `diesel_sqlite_pool` |
|
||||||
//! | Sqlite | [`Rustqlite`](https://github.com/jgallagher/rusqlite) | [`rusqlite::Connection`](https://docs.rs/rusqlite/0.13.0/rusqlite/struct.Connection.html) | `sqlite_pool` | None |
|
//! | Sqlite | [`Rustqlite`] | [`rusqlite::Connection`] | `sqlite_pool` |
|
||||||
//! | Neo4j | [`rusted_cypher`](https://github.com/livioribeiro/rusted-cypher) | [`rusted_cypher::GraphClient`](https://docs.rs/rusted_cypher/1.1.0/rusted_cypher/graph/struct.GraphClient.html) | `cypher_pool` | None |
|
//! | Neo4j | [`rusted_cypher`] | [`rusted_cypher::GraphClient`] | `cypher_pool` |
|
||||||
//! | Redis | [`Redis-rs`](https://github.com/mitsuhiko/redis-rs) | [`redis::Connection`](https://docs.rs/redis/0.9.0/redis/struct.Connection.html) | `redis_pool` | None |
|
//! | Redis | [`redis-rs`] | [`redis::Connection`] | `redis_pool` |
|
||||||
|
//!
|
||||||
|
//! [Diesel]: https://diesel.rs
|
||||||
|
//! [`redis::Connection`]: https://docs.rs/redis/0.9.0/redis/struct.Connection.html
|
||||||
|
//! [`rusted_cypher::GraphClient`]: https://docs.rs/rusted_cypher/1.1.0/rusted_cypher/graph/struct.GraphClient.html
|
||||||
|
//! [`rusqlite::Connection`]: https://docs.rs/rusqlite/0.13.0/rusqlite/struct.Connection.html
|
||||||
|
//! [`diesel::SqliteConnection`]: http://docs.diesel.rs/diesel/prelude/struct.SqliteConnection.html
|
||||||
|
//! [`postgres::Connection`]: https://docs.rs/postgres/0.15.2/postgres/struct.Connection.html
|
||||||
|
//! [`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
|
||||||
|
//! [`mysql::conn`]: https://docs.rs/mysql/14.0.0/mysql/struct.Conn.html
|
||||||
|
//! [`diesel::MysqlConnection`]: http://docs.diesel.rs/diesel/mysql/struct.MysqlConnection.html
|
||||||
|
//! [`redis-rs`]: https://github.com/mitsuhiko/redis-rs
|
||||||
|
//! [`rusted_cypher`]: https://github.com/livioribeiro/rusted-cypher
|
||||||
|
//! [`Rustqlite`]: https://github.com/jgallagher/rusqlite
|
||||||
|
//! [Rust-Postgres]: https://github.com/sfackler/rust-postgres
|
||||||
|
//! [`rust-mysql-simple`]: https://github.com/blackbeam/rust-mysql-simple
|
||||||
|
//! [`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
|
||||||
//!
|
//!
|
||||||
//! ### How to use the table
|
|
||||||
//! The above table lists all the supported database adapters in this library.
|
//! The above table lists all the supported database adapters in this library.
|
||||||
//! In order to use particular `Poolable` type that's included in this library,
|
//! In order to use particular `Poolable` type that's included in this library,
|
||||||
//! you must first enable the feature listed in the 'Feature' column. The inner
|
//! you must first enable the feature listed in the "Feature" column. The
|
||||||
//! type you should use for your database type should be what's listed in the
|
//! interior type of your decorated database type should match the type in the
|
||||||
//! corresponding `Poolable` Type column.
|
//! "`Poolable` Type" column.
|
||||||
//!
|
//!
|
||||||
//! ## Extending
|
//! ## Extending
|
||||||
//!
|
//!
|
||||||
//! Extending Rocket's support to your own custom database adapter (or other
|
//! Extending Rocket's support to your own custom database adapter (or other
|
||||||
//! database-like struct that can be pooled by r2d2) is as easy as implementing
|
//! database-like struct that can be pooled by `r2d2`) is as easy as
|
||||||
//! the `Poolable` trait for your own type. See the documentation for the
|
//! implementing the [`Poolable`] trait. See the documentation for [`Poolable`]
|
||||||
//! [`Poolable` trait](trait.Poolable.html) for more details on how to implement
|
//! for more details on how to implement it.
|
||||||
//! it and extend your type for use with Rocket's database pooling feature.
|
|
||||||
|
|
||||||
pub extern crate r2d2;
|
pub extern crate r2d2;
|
||||||
|
|
||||||
|
@ -292,6 +371,7 @@ use std::marker::{Send, Sized};
|
||||||
|
|
||||||
use rocket::config::{self, Value};
|
use rocket::config::{self, Value};
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
pub use rocket_contrib_codegen::database;
|
pub use rocket_contrib_codegen::database;
|
||||||
|
|
||||||
use self::r2d2::ManageConnection;
|
use self::r2d2::ManageConnection;
|
||||||
|
@ -324,12 +404,12 @@ pub extern crate redis;
|
||||||
#[cfg(feature = "redis_pool")]
|
#[cfg(feature = "redis_pool")]
|
||||||
pub extern crate r2d2_redis;
|
pub extern crate r2d2_redis;
|
||||||
|
|
||||||
/// A struct containing database configuration options from some configuration.
|
/// A structure representing a particular database configuration.
|
||||||
///
|
///
|
||||||
/// For the following configuration:
|
/// For the following configuration:
|
||||||
///
|
///
|
||||||
/// ```toml
|
/// ```toml
|
||||||
/// [[global.databases.my_database]]
|
/// [global.databases.my_database]
|
||||||
/// url = "postgres://root:root@localhost/my_database
|
/// url = "postgres://root:root@localhost/my_database
|
||||||
/// pool_size = 10
|
/// pool_size = 10
|
||||||
/// certs = "sample_cert.pem"
|
/// certs = "sample_cert.pem"
|
||||||
|
@ -337,16 +417,16 @@ pub extern crate r2d2_redis;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The following structure would be generated after calling
|
/// The following structure would be generated after calling
|
||||||
/// `database_config("my_database", &some_config)`:
|
/// [`database_config`]`("my_database", &config)`:
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust,ignore
|
||||||
/// DatabaseConfig {
|
/// DatabaseConfig {
|
||||||
/// url: "dummy_db.sqlite",
|
/// url: "dummy_db.sqlite",
|
||||||
/// pool_size: 10,
|
/// pool_size: 10,
|
||||||
/// extras: {
|
/// extras: {
|
||||||
/// "certs": String("certs.pem"),
|
/// "certs": String("certs.pem"),
|
||||||
/// "key": String("key.pem")
|
/// "key": String("key.pem"),
|
||||||
/// }
|
/// },
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -361,12 +441,10 @@ pub struct DatabaseConfig<'a> {
|
||||||
pub extras: BTreeMap<String, Value>,
|
pub extras: BTreeMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around `r2d2::Error`s or a custom database error type. This type
|
/// A wrapper around `r2d2::Error`s or a custom database error type.
|
||||||
/// is mostly relevant to implementors of the [Poolable](trait.Poolable.html)
|
|
||||||
/// trait.
|
|
||||||
///
|
///
|
||||||
/// Example usages of this type are in the `Poolable` implementations that ship
|
/// This type is only relevant to implementors of the [`Poolable`] trait. See
|
||||||
/// with `rocket_contrib`.
|
/// the [`Poolable`] documentation for more information on how to use this type.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DbError<T> {
|
pub enum DbError<T> {
|
||||||
/// The custom error type to wrap alongside `r2d2::Error`.
|
/// The custom error type to wrap alongside `r2d2::Error`.
|
||||||
|
@ -375,43 +453,42 @@ pub enum DbError<T> {
|
||||||
PoolError(r2d2::Error),
|
PoolError(r2d2::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error type for fetching the DatabaseConfig
|
/// Error returned on invalid database configurations.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum DatabaseConfigError {
|
pub enum DatabaseConfigError {
|
||||||
/// Returned when the `[[global.databases]]` key is missing or empty from
|
/// The `databases` configuration key is missing or is empty.
|
||||||
/// the loaded configuration.
|
|
||||||
MissingTable,
|
MissingTable,
|
||||||
/// Returned when the database configuration key is missing from the active
|
/// The requested database configuration key is missing from the active
|
||||||
/// configuration.
|
/// configuration.
|
||||||
MissingKey,
|
MissingKey,
|
||||||
/// Returned when the configuration associated with the key isn't in the
|
/// The configuration associated with the key isn't a
|
||||||
/// expected [Table](../../rocket/config/type.Table.html) format.
|
/// [Table](/rocket/config/type.Table.html).
|
||||||
MalformedConfiguration,
|
MalformedConfiguration,
|
||||||
/// Returned when the `url` field is missing.
|
/// The required `url` key is missing.
|
||||||
MissingUrl,
|
MissingUrl,
|
||||||
/// Returned when the `url` field is of the wrong type.
|
/// The value for `url` isn't a string.
|
||||||
MalformedUrl,
|
MalformedUrl,
|
||||||
/// Returned when the `pool_size` exceeds `u32::max_value()` or is negative.
|
/// The `pool_size` exceeds `u32::max_value()` or is negative.
|
||||||
InvalidPoolSize(i64),
|
InvalidPoolSize(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method retrieves the database configuration from the loaded
|
/// Retrieves the database configuration for the database named `name`.
|
||||||
/// configuration and returns a [`DatabaseConfig`](struct.DatabaseConfig.html)
|
|
||||||
/// struct.
|
|
||||||
///
|
///
|
||||||
/// # Example:
|
/// This function is primarily used by the code generated by the `#[database]`
|
||||||
|
/// attribute.
|
||||||
///
|
///
|
||||||
/// Given the following configuration:
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Consider the following configuration:
|
||||||
///
|
///
|
||||||
/// ```toml
|
/// ```toml
|
||||||
/// [[global.databases]]
|
/// [global.databases]
|
||||||
/// my_db = { url = "db/db.sqlite", pool_size = 25 }
|
/// my_db = { url = "db/db.sqlite", pool_size = 25 }
|
||||||
/// my_other_db = { url = "mysql://root:root@localhost/database" }
|
/// my_other_db = { url = "mysql://root:root@localhost/database" }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Calling the `database_config` method will return the
|
/// The following example uses `database_config` to retrieve the configurations
|
||||||
/// [`DatabaseConfig`](struct.DatabaseConfig.html) structure for any valid
|
/// for the `my_db` and `my_other_db` databases:
|
||||||
/// configuration key. See the example code below.
|
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate rocket;
|
/// # extern crate rocket;
|
||||||
|
@ -428,29 +505,28 @@ pub enum DatabaseConfigError {
|
||||||
/// # my_db.insert("pool_size".to_string(), Value::from(25));
|
/// # my_db.insert("pool_size".to_string(), Value::from(25));
|
||||||
/// #
|
/// #
|
||||||
/// # let mut my_other_db = BTreeMap::new();
|
/// # let mut my_other_db = BTreeMap::new();
|
||||||
/// # my_other_db.insert("url".to_string(), Value::from("mysql://root:root@localhost/database"));
|
/// # my_other_db.insert("url".to_string(),
|
||||||
|
/// # Value::from("mysql://root:root@localhost/database"));
|
||||||
/// #
|
/// #
|
||||||
/// # databases.insert("my_db".to_string(), Value::from(my_db));
|
/// # databases.insert("my_db".to_string(), Value::from(my_db));
|
||||||
/// # databases.insert("my_other_db".to_string(), Value::from(my_other_db));
|
/// # databases.insert("my_other_db".to_string(), Value::from(my_other_db));
|
||||||
/// #
|
/// #
|
||||||
/// # let config = Config::build(Environment::Development).extra("databases", databases).expect("custom config okay");
|
/// # let config = Config::build(Environment::Development)
|
||||||
|
/// # .extra("databases", databases)
|
||||||
|
/// # .expect("custom config okay");
|
||||||
/// #
|
/// #
|
||||||
/// # rocket::custom(config).attach(AdHoc::on_attach(|rocket| {
|
/// # rocket::custom(config).attach(AdHoc::on_attach("Testing", |rocket| {
|
||||||
/// # // HACK: This is a dirty hack required to be able to make this work
|
/// # {
|
||||||
/// # let thing = {
|
/// let config = database_config("my_db", rocket.config()).unwrap();
|
||||||
/// # let rocket_config = rocket.config();
|
|
||||||
/// let config = database_config("my_db", rocket_config).expect("my_db config okay");
|
|
||||||
/// assert_eq!(config.url, "db/db.sqlite");
|
/// assert_eq!(config.url, "db/db.sqlite");
|
||||||
/// assert_eq!(config.pool_size, 25);
|
/// assert_eq!(config.pool_size, 25);
|
||||||
///
|
///
|
||||||
/// let other_config = database_config("my_other_db", rocket_config).expect("my_other_db config okay");
|
/// let other_config = database_config("my_other_db", rocket.config()).unwrap();
|
||||||
/// assert_eq!(other_config.url, "mysql://root:root@localhost/database");
|
/// assert_eq!(other_config.url, "mysql://root:root@localhost/database");
|
||||||
///
|
///
|
||||||
/// let error = database_config("invalid_db", rocket_config).unwrap_err();
|
/// let error = database_config("invalid_db", rocket.config()).unwrap_err();
|
||||||
/// assert_eq!(error, DatabaseConfigError::MissingKey);
|
/// assert_eq!(error, DatabaseConfigError::MissingKey);
|
||||||
/// #
|
/// # }
|
||||||
/// # 10
|
|
||||||
/// # };
|
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(rocket)
|
/// # Ok(rocket)
|
||||||
/// # }));
|
/// # }));
|
||||||
|
@ -513,112 +589,108 @@ impl<'a> Display for DatabaseConfigError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented by database adapters to allow for r2d2 connection pools to
|
/// Trait implemented by `r2d2`-based database adapters.
|
||||||
/// be easily created.
|
|
||||||
///
|
///
|
||||||
/// # Provided Implementations
|
/// # Provided Implementations
|
||||||
///
|
///
|
||||||
/// Rocket Contrib implements `Poolable` on several common database adapters.
|
/// Implementations of `Poolable` are provided for the following types:
|
||||||
/// The provided implementations are listed here.
|
|
||||||
///
|
///
|
||||||
/// * **diesel::MysqlConnection**
|
/// * `diesel::MysqlConnection`
|
||||||
///
|
/// * `diesel::PgConnection`
|
||||||
/// * **diesel::PgConnection**
|
/// * `diesel::SqliteConnection`
|
||||||
///
|
/// * `postgres::Connection`
|
||||||
/// * **diesel::SqliteConnection**
|
/// * `mysql::Conn`
|
||||||
///
|
/// * `rusqlite::Connection`
|
||||||
/// * **postgres::Connection**
|
/// * `rusted_cypher::GraphClient`
|
||||||
///
|
/// * `redis::Connection`
|
||||||
/// * **mysql::Conn**
|
|
||||||
///
|
|
||||||
/// * **rusqlite::Connection**
|
|
||||||
///
|
|
||||||
/// * **rusted_cypher::GraphClient**
|
|
||||||
///
|
|
||||||
/// * **redis::Connection**
|
|
||||||
///
|
///
|
||||||
/// # Implementation Guide
|
/// # Implementation Guide
|
||||||
///
|
///
|
||||||
/// As a r2d2-compatible database (or other resource) adapter provider,
|
/// As a r2d2-compatible database (or other resource) adapter provider,
|
||||||
/// implementing `Poolable` in your own library will enable Rocket users to
|
/// implementing `Poolable` in your own library will enable Rocket users to
|
||||||
/// consume your adapter with its built-in connection pooling primitives.
|
/// consume your adapter with its built-in connection pooling support.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// This example assumes a `FooConnectionManager` implementing the
|
/// Consider a library `foo` with the following types:
|
||||||
/// `ManageConnection`trait required by r2d2. This connection manager abstracts
|
|
||||||
/// over a pool of `FooClient` connections.
|
|
||||||
///
|
///
|
||||||
/// Given the following definition of the client and connection manager:
|
/// * `foo::ConnectionManager`, which implements [`r2d2::ManageConnection`]
|
||||||
|
/// * `foo::Connection`, the `Connection` associated type of
|
||||||
|
/// `foo::ConnectionManager`
|
||||||
|
/// * `foo::Error`, errors resulting from manager instantiation
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// [`r2d2`]: https://crates.io/crates/r2d2
|
||||||
/// struct FooClient { ... };
|
/// [`r2d2::ManageConnection`]: http://docs.rs/r2d2/0.8/r2d2/trait.ManageConnection.html
|
||||||
///
|
///
|
||||||
/// impl FooClient {
|
/// In order for Rocket to generate the required code to automatically provision
|
||||||
/// pub fn new(...) -> Result<Self, foo::Error> {
|
/// a r2d2 connection pool into application state, the `Poolable` trait needs to
|
||||||
/// ...
|
/// be implemented for the connection type. The following example implements
|
||||||
/// }
|
/// `Poolable` for `foo::Connection`:
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// struct FooConnectionManager { ... };
|
/// ```rust
|
||||||
|
/// use rocket_contrib::databases::{r2d2, DbError, DatabaseConfig, Poolable};
|
||||||
///
|
///
|
||||||
/// impl FooConnectionManager {
|
/// # mod foo {
|
||||||
/// pub fn new(...) -> Result<Self, foo::Error> {
|
/// # use rocket_contrib::databases::r2d2;
|
||||||
/// ...
|
/// # use std::fmt;
|
||||||
/// }
|
/// # #[derive(Debug)] pub struct Error;
|
||||||
/// }
|
/// # impl ::std::error::Error for Error { }
|
||||||
/// ```
|
/// # impl fmt::Display for Error {
|
||||||
///
|
/// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
|
||||||
/// In order to allow for Rocket Contrib to generate the required code to
|
/// # }
|
||||||
/// automatically provision a r2d2 connection pool into application state, the
|
/// #
|
||||||
/// `Poolable` trait needs to be implemented for the connection type.
|
/// # pub struct Connection;
|
||||||
///
|
/// # pub struct ConnectionManager;
|
||||||
/// Given the above definitions, the following would be a valid implementation
|
/// #
|
||||||
/// of the `Poolable` trait:
|
/// # type Result<T> = ::std::result::Result<T, Error>;
|
||||||
///
|
/// #
|
||||||
/// ```rust,ignore
|
/// # impl self::r2d2::ManageConnection for ConnectionManager {
|
||||||
/// impl Poolable for FooClient {
|
/// # type Connection = Connection;
|
||||||
/// type Manager = FooConnectionManager;
|
/// # type Error = Error;
|
||||||
|
/// # fn connect(&self) -> Result<Connection> { panic!(()) }
|
||||||
|
/// # fn is_valid(&self, _: &mut Connection) -> Result<()> { panic!() }
|
||||||
|
/// # fn has_broken(&self, _: &mut Connection) -> bool { panic!() }
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// impl Poolable for foo::Connection {
|
||||||
|
/// type Manager = foo::ConnectionManager;
|
||||||
/// type Error = DbError<foo::Error>;
|
/// type Error = DbError<foo::Error>;
|
||||||
///
|
///
|
||||||
/// fn pool(config: DatabaseConfig) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
/// fn pool(config: DatabaseConfig) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||||
/// let manager = FooConnectionManager::new(config.url)
|
/// # let _ = config; /*
|
||||||
|
/// let manager = foo::ConnectionManager::new(config.url)
|
||||||
/// .map_err(DbError::Custom)?;
|
/// .map_err(DbError::Custom)?;
|
||||||
///
|
///
|
||||||
/// r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
/// r2d2::Pool::builder()
|
||||||
|
/// .max_size(config.pool_size)
|
||||||
|
/// .build(manager)
|
||||||
/// .map_err(DbError::PoolError)
|
/// .map_err(DbError::PoolError)
|
||||||
|
/// # */
|
||||||
|
/// # Err(DbError::Custom(foo::Error))
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// In the above example, the connection manager is failable and returns the the
|
/// In this example, `ConnectionManager::new()` method returns a `foo::Error` on
|
||||||
/// `FooClient`'s error type. Since the error type can diverge from a simple
|
/// failure. For convenience, the [`DbError`] enum is used to consolidate this
|
||||||
/// r2d2 pool error, the [`DbError`](enum.DbError.html) wrapper is used. This
|
/// error type and the `r2d2::Error` type that can result from
|
||||||
/// error type is defined as part of the associated type in the `Poolable` trait
|
/// `r2d2::Pool::builder()`.
|
||||||
/// definition.
|
|
||||||
///
|
///
|
||||||
/// Additionally, you'll notice that the `pool` method of the trait is used to
|
/// In the event that a connection manager isn't fallible (as is the case with
|
||||||
/// to create the connection manager and the pool. This method returns a
|
/// Diesel's r2d2 connection manager, for instance), the associated error type
|
||||||
/// `Result` containing an r2d2 pool monomorphized to the `Manager` associated
|
|
||||||
/// type in the trait definition, or containing the `Error` associated type.
|
|
||||||
///
|
|
||||||
/// In the event that the connection manager isn't failable (as is the case in
|
|
||||||
/// Diesel's r2d2 connection manager, for example), the associated error type
|
|
||||||
/// for the `Poolable` implementation can simply be `r2d2::Error` as this is the
|
/// for the `Poolable` implementation can simply be `r2d2::Error` as this is the
|
||||||
/// only error that can be returned by the `pool` method. You can refer to the
|
/// only error that can be result. For more concrete example, consult Rocket's
|
||||||
/// included implementations of `Poolable` in the `rocket_contrib::databases`
|
/// existing implementations of [`Poolable`].
|
||||||
/// module for concrete examples.
|
|
||||||
///
|
|
||||||
pub trait Poolable: Send + Sized + 'static {
|
pub trait Poolable: Send + Sized + 'static {
|
||||||
/// The associated connection manager for the given connection type.
|
/// The associated connection manager for the given connection type.
|
||||||
type Manager: ManageConnection<Connection=Self>;
|
type Manager: ManageConnection<Connection=Self>;
|
||||||
/// The associated error type in the event that constructing the connection
|
/// The associated error type in the event that constructing the connection
|
||||||
/// manager and/or the connection pool fails
|
/// manager and/or the connection pool fails.
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
/// Creates an r2d2 connection pool from the provided Manager associated
|
/// Creates an `r2d2` connection pool for `Manager::Connection`, returning
|
||||||
/// type and returns the pool or the error associated with the trait
|
/// the pool on success.
|
||||||
/// implementation.
|
|
||||||
fn pool(config: DatabaseConfig) -> Result<r2d2::Pool<Self::Manager>, Self::Error>;
|
fn pool(config: DatabaseConfig) -> Result<r2d2::Pool<Self::Manager>, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,7 +793,7 @@ impl Poolable for redis::Connection {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use rocket::{Config, config::{Environment, Value}};
|
use rocket::{Config, config::{Environment, Value}};
|
||||||
use super::{DatabaseConfigError, database_config};
|
use super::{DatabaseConfigError::*, database_config};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_database_entry_in_config_returns_error() {
|
fn no_database_entry_in_config_returns_error() {
|
||||||
|
@ -730,7 +802,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::MissingTable), database_config_result);
|
assert_eq!(Err(MissingTable), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -749,7 +821,7 @@ mod tests {
|
||||||
|
|
||||||
let database_config_result = database_config("real_db", &config);
|
let database_config_result = database_config("real_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::MissingKey), database_config_result);
|
assert_eq!(Err(MissingKey), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -765,7 +837,7 @@ mod tests {
|
||||||
|
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::MalformedConfiguration), database_config_result);
|
assert_eq!(Err(MalformedConfiguration), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -781,7 +853,7 @@ mod tests {
|
||||||
|
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::MissingUrl), database_config_result);
|
assert_eq!(Err(MissingUrl), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -798,7 +870,7 @@ mod tests {
|
||||||
|
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::MalformedUrl), database_config_result);
|
assert_eq!(Err(MalformedUrl), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -816,15 +888,16 @@ mod tests {
|
||||||
|
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
assert_eq!(Err(DatabaseConfigError::InvalidPoolSize(-1)), database_config_result);
|
assert_eq!(Err(InvalidPoolSize(-1)), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pool_size_beyond_u32_max_returns_error() {
|
fn pool_size_beyond_u32_max_returns_error() {
|
||||||
let mut database_extra = BTreeMap::new();
|
let mut database_extra = BTreeMap::new();
|
||||||
let mut connection_config = BTreeMap::new();
|
let mut connection_config = BTreeMap::new();
|
||||||
|
let over_max = (u32::max_value()) as i64 + 1;
|
||||||
connection_config.insert("url".to_string(), Value::from("dummy_db.sqlite"));
|
connection_config.insert("url".to_string(), Value::from("dummy_db.sqlite"));
|
||||||
connection_config.insert("pool_size".to_string(), Value::from(4294967296));
|
connection_config.insert("pool_size".to_string(), Value::from(over_max));
|
||||||
database_extra.insert("dummy_db", connection_config);
|
database_extra.insert("dummy_db", connection_config);
|
||||||
|
|
||||||
let config = Config::build(Environment::Development)
|
let config = Config::build(Environment::Development)
|
||||||
|
@ -835,7 +908,7 @@ mod tests {
|
||||||
let database_config_result = database_config("dummy_db", &config);
|
let database_config_result = database_config("dummy_db", &config);
|
||||||
|
|
||||||
// The size of `0` is an overflow wrap-around
|
// The size of `0` is an overflow wrap-around
|
||||||
assert_eq!(Err(DatabaseConfigError::InvalidPoolSize(0)), database_config_result);
|
assert_eq!(Err(InvalidPoolSize(over_max)), database_config_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
//! * [handlebars_templates](struct.Template.html)
|
//! * [handlebars_templates](struct.Template.html)
|
||||||
//! * [tera_templates](struct.Template.html)
|
//! * [tera_templates](struct.Template.html)
|
||||||
//! * [uuid](struct.Uuid.html)
|
//! * [uuid](struct.Uuid.html)
|
||||||
//! * [database_pool](databases/index.html)
|
//! * [${database}_pool](databases/index.html)
|
||||||
//!
|
//!
|
||||||
//! The recommend way to include features from this crate via Cargo in your
|
//! The recommend way to include features from this crate via Cargo in your
|
||||||
//! project is by adding a `[dependencies.rocket_contrib]` section to your
|
//! project is by adding a `[dependencies.rocket_contrib]` section to your
|
||||||
|
|
|
@ -154,8 +154,6 @@ impl<'a, S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
|
||||||
/// A `FromData` implementation allowing this looks like:
|
/// A `FromData` implementation allowing this looks like:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #![allow(unused_attributes)]
|
|
||||||
/// # #![allow(unused_variables)]
|
|
||||||
/// # #![feature(plugin, decl_macro)]
|
/// # #![feature(plugin, decl_macro)]
|
||||||
/// # #![plugin(rocket_codegen)]
|
/// # #![plugin(rocket_codegen)]
|
||||||
/// # extern crate rocket;
|
/// # extern crate rocket;
|
||||||
|
|
|
@ -212,6 +212,10 @@ pub fn init(level: LoggingLevel) -> bool {
|
||||||
// This method exists as a shim for the log macros that need to be called from
|
// This method exists as a shim for the log macros that need to be called from
|
||||||
// an end user's code. It was added as part of the work to support database
|
// an end user's code. It was added as part of the work to support database
|
||||||
// connection pools via procedural macros.
|
// connection pools via procedural macros.
|
||||||
pub fn log_err(msg: &str) {
|
#[doc(hidden)]
|
||||||
error!("{}", msg);
|
pub fn log_err(indented: bool, msg: &str) {
|
||||||
|
match indented {
|
||||||
|
true => error_!("{}", msg),
|
||||||
|
false => error!("{}", msg),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,4 +19,4 @@ rand = "0.5"
|
||||||
[dependencies.rocket_contrib]
|
[dependencies.rocket_contrib]
|
||||||
path = "../../contrib/lib"
|
path = "../../contrib/lib"
|
||||||
default_features = false
|
default_features = false
|
||||||
features = [ "tera_templates", "database_pool", "diesel_sqlite_pool" ]
|
features = ["tera_templates", "diesel_sqlite_pool"]
|
||||||
|
|
|
@ -23,6 +23,6 @@ following commands:
|
||||||
# install Diesel CLI tools
|
# install Diesel CLI tools
|
||||||
cargo install diesel_cli --version '<= 1.2' --no-default-features --features=sqlite
|
cargo install diesel_cli --version '<= 1.2' --no-default-features --features=sqlite
|
||||||
|
|
||||||
# create db/db.sql
|
# create db/db.sqlite
|
||||||
diesel migration run --database-url="db/db.sql"
|
diesel migration run --database-url="db/db.sqlite"
|
||||||
```
|
```
|
||||||
|
|
|
@ -12,6 +12,6 @@ pushd "${SCRIPT_PATH}" > /dev/null
|
||||||
cargo install diesel_cli --version '<= 1.2' --no-default-features --features=sqlite > /dev/null
|
cargo install diesel_cli --version '<= 1.2' --no-default-features --features=sqlite > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# create db/db.sql
|
# create db/db.sqlite
|
||||||
diesel migration --database-url="${DATABASE_URL}" run > /dev/null
|
diesel migration --database-url="${DATABASE_URL}" run > /dev/null
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
|
@ -147,141 +147,112 @@ to implement request timing.
|
||||||
|
|
||||||
## Databases
|
## Databases
|
||||||
|
|
||||||
While Rocket doesn't have built-in support for databases yet, you can combine a
|
Rocket includes built-in, ORM-agnostic support for databases. In particular,
|
||||||
few external libraries to get native-feeling access to databases in a Rocket
|
Rocket provides a procedural macro that allows you to easily connect your Rocket
|
||||||
application. Let's take a look at how we might integrate Rocket with two common
|
application to databases through connection pools. A _database connection pool_
|
||||||
database libraries: [`diesel`], a type-safe ORM and query builder, and [`r2d2`],
|
is a data structure that maintains active database connections for later use in
|
||||||
a library for connection pooling.
|
the application. This implementation of connection pooling support is based on
|
||||||
|
[`r2d2`] and exposes connections through request guards. Databases are
|
||||||
|
individually configured through Rocket's regular configuration mechanisms: a
|
||||||
|
`Rocket.toml` file, environment variables, or procedurally.
|
||||||
|
|
||||||
Our approach will be to have Rocket manage a pool of database connections using
|
Connecting your Rocket application to a database using this library occurs in
|
||||||
managed state and then implement a request guard that retrieves one connection.
|
three simple steps:
|
||||||
This will allow us to get access to the database in a handler by simply adding a
|
|
||||||
`DbConn` argument:
|
1. Configure the databases in `Rocket.toml`.
|
||||||
|
2. Associate a request guard type and fairing with each database.
|
||||||
|
3. Use the request guard to retrieve a connection in a handler.
|
||||||
|
|
||||||
|
Presently, Rocket provides built-in support for the following databases:
|
||||||
|
|
||||||
|
| Kind | Driver | `Poolable` Type | Feature |
|
||||||
|
|----------|-----------------------|--------------------------------|------------------------|
|
||||||
|
| MySQL | [Diesel] | [`diesel::MysqlConnection`] | `diesel_mysql_pool` |
|
||||||
|
| MySQL | [`rust-mysql-simple`] | [`mysql::conn`] | `mysql_pool` |
|
||||||
|
| Postgres | [Diesel] | [`diesel::PgConnection`] | `diesel_postgres_pool` |
|
||||||
|
| Postgres | [Rust-Postgres] | [`postgres::Connection`] | `postgres_pool` |
|
||||||
|
| Sqlite | [Diesel] | [`diesel::SqliteConnection`] | `diesel_sqlite_pool` |
|
||||||
|
| Sqlite | [`Rustqlite`] | [`rusqlite::Connection`] | `sqlite_pool` |
|
||||||
|
| Neo4j | [`rusted_cypher`] | [`rusted_cypher::GraphClient`] | `cypher_pool` |
|
||||||
|
| Redis | [`redis-rs`] | [`redis::Connection`] | `redis_pool` |
|
||||||
|
|
||||||
|
[`r2d2`]: https://crates.io/crates/r2d2
|
||||||
|
[Diesel]: https://diesel.rs
|
||||||
|
[`redis::Connection`]: https://docs.rs/redis/0.9.0/redis/struct.Connection.html
|
||||||
|
[`rusted_cypher::GraphClient`]: https://docs.rs/rusted_cypher/1.1.0/rusted_cypher/graph/struct.GraphClient.html
|
||||||
|
[`rusqlite::Connection`]: https://docs.rs/rusqlite/0.13.0/rusqlite/struct.Connection.html
|
||||||
|
[`diesel::SqliteConnection`]: http://docs.diesel.rs/diesel/prelude/struct.SqliteConnection.html
|
||||||
|
[`postgres::Connection`]: https://docs.rs/postgres/0.15.2/postgres/struct.Connection.html
|
||||||
|
[`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
|
||||||
|
[`mysql::conn`]: https://docs.rs/mysql/14.0.0/mysql/struct.Conn.html
|
||||||
|
[`diesel::MysqlConnection`]: http://docs.diesel.rs/diesel/mysql/struct.MysqlConnection.html
|
||||||
|
[`redis-rs`]: https://github.com/mitsuhiko/redis-rs
|
||||||
|
[`rusted_cypher`]: https://github.com/livioribeiro/rusted-cypher
|
||||||
|
[`Rustqlite`]: https://github.com/jgallagher/rusqlite
|
||||||
|
[Rust-Postgres]: https://github.com/sfackler/rust-postgres
|
||||||
|
[`rust-mysql-simple`]: https://github.com/blackbeam/rust-mysql-simple
|
||||||
|
[`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
To connect your Rocket application to a given database, first identify the
|
||||||
|
"Kind" and "Driver" in the table that matches your environment. The feature
|
||||||
|
corresponding to your database type must be enabled. This is the feature
|
||||||
|
identified in the "Feature" column. For instance, for Diesel-based SQLite
|
||||||
|
databases, you'd write in `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies.rocket_contrib]
|
||||||
|
version = "0.4.0-dev"
|
||||||
|
default-features = false
|
||||||
|
features = ["diesel_sqlite_pool"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in `Rocket.toml` or the equivalent via environment variables, configure
|
||||||
|
the URL for the database in the `databases` table:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[global.databases]
|
||||||
|
sqlite_logs = { url = "/path/to/database.sqlite" }
|
||||||
|
```
|
||||||
|
|
||||||
|
In your application's source code, create a unit-like struct with one internal
|
||||||
|
type. This type should be the type listed in the "`Poolable` Type" column. Then
|
||||||
|
decorate the type with the `#[database]` attribute, providing the name of the
|
||||||
|
database that you configured in the previous step as the only parameter.
|
||||||
|
Finally, attach the fairing returned by `YourType::fairing()`, which was
|
||||||
|
generated by the `#[database]` attribute:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[get("/users")]
|
use rocket_contrib::databases::{database, diesel};
|
||||||
fn handler(conn: DbConn) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
[`diesel`]: http://diesel.rs/
|
#[database("sqlite_logs")]
|
||||||
[`r2d2`]: https://docs.rs/r2d2/
|
struct LogsDbConn(diesel::SqliteConnection);
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
To get started, we need to depend on the `diesel` and `r2d2` crates. For
|
|
||||||
detailed information on how to use Diesel, please see the [Diesel getting
|
|
||||||
started guide](http://diesel.rs/guides/getting-started/). For this example, we
|
|
||||||
use the following dependencies:
|
|
||||||
|
|
||||||
```
|
|
||||||
[dependencies]
|
|
||||||
rocket = "0.4.0-dev"
|
|
||||||
rocket_codegen = "0.4.0-dev"
|
|
||||||
diesel = { version = "<= 1.2", features = ["sqlite", "r2d2"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
Your `diesel` dependency information may differ. The crates are imported as
|
|
||||||
well:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
extern crate rocket;
|
|
||||||
#[macro_use] extern crate diesel;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Managed Pool
|
|
||||||
|
|
||||||
The first step is to initialize a pool of database connections. The `init_pool`
|
|
||||||
function below uses `r2d2` to create a new pool of database connections. Diesel
|
|
||||||
advocates for using a `DATABASE_URL` environment variable to set the database
|
|
||||||
URL, and we use the same convention here. Excepting the long-winded types, the
|
|
||||||
code is fairly straightforward: the `DATABASE_URL` environment variable is
|
|
||||||
stored in the `DATABASE_URL` static, and an `r2d2::Pool` is created using the
|
|
||||||
default configuration parameters and a Diesel `SqliteConnection`
|
|
||||||
`ConnectionManager`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use diesel::sqlite::SqliteConnection;
|
|
||||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
|
||||||
|
|
||||||
// An alias to the type for a pool of Diesel SQLite connections.
|
|
||||||
type SqlitePool = Pool<ConnectionManager<SqliteConnection>>;
|
|
||||||
|
|
||||||
// The URL to the database, set via the `DATABASE_URL` environment variable.
|
|
||||||
static DATABASE_URL: &'static str = env!("DATABASE_URL");
|
|
||||||
|
|
||||||
/// Initializes a database pool.
|
|
||||||
fn init_pool() -> SqlitePool {
|
|
||||||
let manager = ConnectionManager::<SqliteConnection>::new(DATABASE_URL);
|
|
||||||
Pool::new(manager).expect("db pool")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We then use managed state to have Rocket manage the pool for us:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn main() {
|
fn main() {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.manage(init_pool())
|
.attach(LogsDbConn::fairing())
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Connection Guard
|
That's it! Whenever a connection to the database is needed, use your type as a
|
||||||
|
request guard:
|
||||||
The second and final step is to implement a request guard that retrieves a
|
|
||||||
single connection from the managed connection pool. We create a new type,
|
|
||||||
`DbConn`, that wraps an `r2d2` pooled connection. We then implement
|
|
||||||
`FromRequest` for `DbConn` so that we can use it as a request guard. Finally, we
|
|
||||||
implement `Deref` with a target of `SqliteConnection` so that we can
|
|
||||||
transparently use an `&*DbConn` as an `&SqliteConnection`.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::ops::Deref;
|
impl Logs {
|
||||||
use rocket::http::Status;
|
fn by_id(conn: &diesel::SqliteConnection, log_id: usize) -> Result<Logs> {
|
||||||
use rocket::request::{self, FromRequest};
|
logs.filter(id.eq(log_id)).load(conn)
|
||||||
use rocket::{Request, State, Outcome};
|
|
||||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
|
||||||
|
|
||||||
// Connection request guard type: a wrapper around an r2d2 pooled connection.
|
|
||||||
pub struct DbConn(pub PooledConnection<ConnectionManager<SqliteConnection>>);
|
|
||||||
|
|
||||||
/// Attempts to retrieve a single connection from the managed database pool. If
|
|
||||||
/// no pool is currently managed, fails with an `InternalServerError` status. If
|
|
||||||
/// no connections are available, fails with a `ServiceUnavailable` status.
|
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
|
||||||
let pool = request.guard::<State<SqlitePool>>()?;
|
|
||||||
match pool.get() {
|
|
||||||
Ok(conn) => Outcome::Success(DbConn(conn)),
|
|
||||||
Err(_) => Outcome::Failure((Status::ServiceUnavailable, ()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the convenience of using an &DbConn as an &SqliteConnection.
|
#[get("/logs/<id>")]
|
||||||
impl Deref for DbConn {
|
fn get_logs(conn: LogsDbConn, id: usize) -> Result<Logs> {
|
||||||
type Target = SqliteConnection;
|
Logs::by_id(&conn, id)
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Usage
|
For more on Rocket's built-in database support, see the
|
||||||
|
[`rocket_contrib::databases`] module documentation.
|
||||||
|
|
||||||
With these two pieces in place, we can use `DbConn` as a request guard in any
|
[`rocket_contrib::databases`]: https://api.rocket.rs/rocket_contrib/databases/index.html
|
||||||
handler or other request guard implementation, giving our application access to
|
|
||||||
a database. As a simple example, we might write a route that returns a JSON
|
|
||||||
array of some `Task` structures that are fetched from a database:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[get("/tasks")]
|
|
||||||
fn get_tasks(conn: DbConn) -> QueryResult<Json<Vec<Task>>> {
|
|
||||||
all_tasks.order(tasks::id.desc())
|
|
||||||
.load::<Task>(&*conn)
|
|
||||||
.map(|tasks| Json(tasks))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
Loading…
Reference in New Issue