diesel::expression

Trait BoxableExpression

Source
pub trait BoxableExpression<QS, DB, GB = (), IsAggregate = No>
where DB: Backend, Self: Expression + SelectableExpression<QS> + QueryFragment<DB> + Send,
{ }
Expand description

Helper trait used when boxing expressions.

In Rust you cannot create a trait object with more than one trait. This type has all of the additional traits you would want when using Box<Expression> as a single trait object.

By default BoxableExpression is not usable in queries that have a custom group by clause. Setting the generic parameters GB and IsAggregate allows to configure the expression to be used with a specific group by clause.

This is typically used as the return type of a function. For cases where you want to dynamically construct a query, boxing the query is usually more ergonomic.

§Examples

§Usage without group by clause

use diesel::sql_types::Bool;

enum Search {
    Id(i32),
    Name(String),
}

type DB = diesel::sqlite::Sqlite;

fn find_user(search: Search) -> Box<dyn BoxableExpression<users::table, DB, SqlType = Bool>> {
    match search {
        Search::Id(id) => Box::new(users::id.eq(id)),
        Search::Name(name) => Box::new(users::name.eq(name)),
    }
}

let user_one = users::table
    .filter(find_user(Search::Id(1)))
    .first(conn)?;
assert_eq!((1, String::from("Sean")), user_one);

let tess = users::table
    .filter(find_user(Search::Name("Tess".into())))
    .first(conn)?;
assert_eq!((2, String::from("Tess")), tess);

§Allow usage with group by clause


use diesel::sql_types::Text;
use diesel::dsl;
use diesel::expression::ValidGrouping;

enum NameOrConst {
    Name,
    Const(String),
}

type DB = diesel::sqlite::Sqlite;

fn selection<GB>(
    selection: NameOrConst
) -> Box<
    dyn BoxableExpression<
        users::table,
        DB,
        GB,
        <users::name as ValidGrouping<GB>>::IsAggregate,
        SqlType = Text
    >
>
where
    users::name: BoxableExpression<
            users::table,
            DB,
            GB,
            <users::name as ValidGrouping<GB>>::IsAggregate,
            SqlType = Text
        > + ValidGrouping<GB>,
{
    match selection {
        NameOrConst::Name => Box::new(users::name),
        NameOrConst::Const(name) => Box::new(name.into_sql::<Text>()),
    }
}

let user_one = users::table
    .select(selection(NameOrConst::Name))
    .first::<String>(conn)?;
assert_eq!(String::from("Sean"), user_one);

let with_name = users::table
    .group_by(users::name)
    .select(selection(NameOrConst::Const("Jane Doe".into())))
    .first::<String>(conn)?;
assert_eq!(String::from("Jane Doe"), with_name);

§More advanced query source

This example is a bit contrived, but in general, if you want to for example filter based on different criteria on a joined table, you can use InnerJoinQuerySource and LeftJoinQuerySource in the QS parameter of BoxableExpression.

use diesel::sql_types::Bool;
use diesel::dsl::InnerJoinQuerySource;

enum UserPostFilter {
    User(i32),
    Post(i32),
}

type DB = diesel::sqlite::Sqlite;

fn filter_user_posts(
    filter: UserPostFilter,
) -> Box<dyn BoxableExpression<InnerJoinQuerySource<users::table, posts::table>, DB, SqlType = Bool>>
{
    match filter {
        UserPostFilter::User(user_id) => Box::new(users::id.eq(user_id)),
        UserPostFilter::Post(post_id) => Box::new(posts::id.eq(post_id)),
    }
}

let post_by_user_one = users::table
    .inner_join(posts::table)
    .filter(filter_user_posts(UserPostFilter::User(2)))
    .select((posts::title, users::name))
    .first::<(String, String)>(conn)?;

assert_eq!(
    ("My first post too".to_string(), "Tess".to_string()),
    post_by_user_one
);

Trait Implementations§

Source§

impl<QS, ST, DB, GB, IsAggregate> QueryId for dyn BoxableExpression<QS, DB, GB, IsAggregate, SqlType = ST> + '_

Source§

const HAS_STATIC_QUERY_ID: bool = false

Can the SQL generated by Self be uniquely identified by its type? Read more
Source§

type QueryId = ()

A type which uniquely represents Self in a SQL query. Read more
Source§

fn query_id() -> Option<TypeId>

Returns the type id of Self::QueryId if Self::HAS_STATIC_QUERY_ID. Returns None otherwise. Read more
Source§

impl<QS, ST, DB, GB, IsAggregate> ValidGrouping<GB> for dyn BoxableExpression<QS, DB, GB, IsAggregate, SqlType = ST> + '_

Source§

type IsAggregate = IsAggregate

Is this expression aggregate? Read more

Implementors§

Source§

impl<QS, T, DB, GB, IsAggregate> BoxableExpression<QS, DB, GB, IsAggregate> for T
where DB: Backend, T: Expression + SelectableExpression<QS> + ValidGrouping<GB> + QueryFragment<DB> + Send, T::IsAggregate: MixedAggregates<IsAggregate, Output = IsAggregate>,