diesel

Derive Macro MultiConnection

source
#[derive(MultiConnection)]
Expand description

This derives implements diesel::Connection and related traits for an enum of connections to different databases.

By applying this derive to such an enum, you can use the enum as a connection type in any location all the inner connections are valid. This derive supports enum variants containing a single tuple field. Each tuple field type must implement diesel::Connection and a number of related traits. Connection types form Diesel itself as well as third party connection types are supported by this derive.

The implementation of diesel::Connection::establish tries to establish a new connection with the given connection string in the order the connections are specified in the enum. If one connection fails, it tries the next one and so on. That means that as soon as more than one connection type accepts a certain connection string the first matching type in your enum will always establish the connection. This is especially important if one of the connection types is diesel::SqliteConnection as this connection type accepts arbitrary paths. It should normally place as last entry in your enum. If you want control of which connection type is created, just construct the corresponding enum manually by first establishing the connection via the inner type and then wrap the result into the enum.

§Example

use diesel::prelude::*;

#[derive(diesel::MultiConnection)]
pub enum AnyConnection {
    Postgresql(diesel::PgConnection),
    Mysql(diesel::MysqlConnection),
    Sqlite(diesel::SqliteConnection),
}

diesel::table! {
    users {
        id -> Integer,
        name -> Text,
    }
}

fn use_multi(conn: &mut AnyConnection) -> QueryResult<()> {
   // Use the connection enum as any other connection type
   // for inserting/updating/loading/…
   diesel::insert_into(users::table)
       .values(users::name.eq("Sean"))
       .execute(conn)?;

   let users = users::table.load::<(i32, String)>(conn)?;

   // Match on the connection type to access
   // the inner connection. This allows us then to use
   // backend specific methods.
   if let AnyConnection::Postgresql(ref mut conn) = conn {
       // perform a postgresql specific query here
       let users = users::table.load::<(i32, String)>(conn)?;
   }

   Ok(())
}

§Limitations

The derived connection implementation can only cover the common subset of all inner connection types. So, if one backend doesn’t support certain SQL features, like for example, returning clauses, the whole connection implementation doesn’t support this feature. In addition, only a limited set of SQL types is supported:

  • diesel::sql_types::SmallInt
  • diesel::sql_types::Integer
  • diesel::sql_types::BigInt
  • diesel::sql_types::Double
  • diesel::sql_types::Float
  • diesel::sql_types::Text
  • diesel::sql_types::Date
  • diesel::sql_types::Time
  • diesel::sql_types::Timestamp

Support for additional types can be added by providing manual implementations of HasSqlType, FromSql and ToSql for the corresponding type, all databases included in your enum, and the backend generated by this derive called MultiBackend. For example to support a custom enum MyEnum with the custom SQL type MyInteger:

extern crate diesel;
use diesel::backend::Backend;
use diesel::deserialize::{self, FromSql, FromSqlRow};
use diesel::serialize::{self, IsNull, ToSql};
use diesel::AsExpression;
use diesel::sql_types::{HasSqlType, SqlType};
use diesel::prelude::*;

#[derive(diesel::MultiConnection)]
pub enum AnyConnection {
    Postgresql(diesel::PgConnection),
    Mysql(diesel::MysqlConnection),
    Sqlite(diesel::SqliteConnection),
}

// defining an custom SQL type is optional
// you can also use types from `diesel::sql_types`
#[derive(Copy, Clone, Debug, SqlType)]
#[diesel(postgres_type(name = "Int4"))]
#[diesel(mysql_type(name = "Long"))]
#[diesel(sqlite_type(name = "Integer"))]
struct MyInteger;


// our custom enum
#[repr(i32)]
#[derive(Debug, Clone, Copy, AsExpression, FromSqlRow)]
#[diesel(sql_type = MyInteger)]
pub enum MyEnum {
    A = 1,
    B = 2,
}

// The `MultiBackend` type is generated by `#[derive(diesel::MultiConnection)]`
// This part is only required if you define a custom sql type
impl HasSqlType<MyInteger> for MultiBackend {
   fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata {
       // The `lookup_sql_type` function is exposed by the `MultiBackend` type
       MultiBackend::lookup_sql_type::<MyInteger>(lookup)
   }
}

impl FromSql<MyInteger, MultiBackend> for MyEnum {
   fn from_sql(bytes: <MultiBackend as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
       // The `from_sql` function is exposed by the `RawValue` type of the
       // `MultiBackend` type
       // This requires a `FromSql` impl for each backend
       bytes.from_sql::<MyEnum, MyInteger>()
   }
}

impl ToSql<MyInteger, MultiBackend> for MyEnum {
   fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, MultiBackend>) -> serialize::Result {
       /// `set_value` expects a tuple consisting of the target SQL type
       /// and self for `MultiBackend`
       /// This requires a `ToSql` impl for each backend
       out.set_value((MyInteger, self));
       Ok(IsNull::No)
   }
}