diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b68aa9931d..d7bca7803d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,11 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/ [insert]: http://docs.diesel.rs/diesel/fn.insert.html +* Added a function for SQL `EXISTS` expressions. See + [`diesel::expression::dsl::exists`][exists] for details. + +[exists]: http://docs.diesel.rs/diesel/expression/dsl/fn.sql.html + ### Changed * All macros with the same name as traits we can derive (e.g. `Queryable!`) have diff --git a/diesel/src/expression/exists.rs b/diesel/src/expression/exists.rs new file mode 100644 index 000000000000..f0047369fc4b --- /dev/null +++ b/diesel/src/expression/exists.rs @@ -0,0 +1,81 @@ +use backend::Backend; +use expression::{Expression, SelectableExpression, NonAggregate}; +use query_builder::*; +use result::QueryResult; +use types::Bool; + +/// Creates a SQL `EXISTS` expression. +/// +/// The argument must be a complete SQL query. The result of this could in +/// theory be passed to `.filter`, but since the query cannot reference columns +/// from the outer query, this is of limited usefulness. +/// +/// # Example +/// +/// ```rust +/// # #[macro_use] extern crate diesel; +/// # include!("src/doctest_setup.rs"); +/// # +/// # table! { +/// # users { +/// # id -> Integer, +/// # name -> VarChar, +/// # } +/// # } +/// # +/// # fn main() { +/// # use self::users::dsl::*; +/// # use diesel::select; +/// # use diesel::expression::dsl::exists; +/// # let connection = establish_connection(); +/// let sean_exists = select(exists(users.filter(name.eq("Sean")))) +/// .get_result(&connection); +/// let jim_exists = select(exists(users.filter(name.eq("Jim")))) +/// .get_result(&connection); +/// assert_eq!(Ok(true), sean_exists); +/// assert_eq!(Ok(false), jim_exists); +/// # } +/// ``` +pub fn exists(query: T) -> Exists { + Exists(query.as_query()) +} + +#[derive(Debug, Clone, Copy)] +pub struct Exists(T); + +impl Expression for Exists where + T: Query, +{ + type SqlType = Bool; +} + +impl SelectableExpression for Exists where + Exists: Expression, +{ +} + +impl NonAggregate for Exists { +} + +impl QueryFragment for Exists where + DB: Backend, + T: QueryFragment, +{ + fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + out.push_sql("EXISTS ("); + try!(self.0.to_sql(out)); + out.push_sql(")"); + Ok(()) + } + + fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + try!(self.0.collect_binds(out)); + Ok(()) + } + + fn is_safe_to_cache_prepared(&self) -> bool { + self.0.is_safe_to_cache_prepared() + } +} + +impl_query_id!(Exists); diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index 0d2dce97cc98..85999042fc83 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -25,6 +25,8 @@ pub mod array_comparison; pub mod bound; #[doc(hidden)] pub mod count; +#[doc(hidden)] +pub mod exists; pub mod expression_methods; #[doc(hidden)] pub mod functions; @@ -47,6 +49,7 @@ pub mod dsl { #[doc(inline)] pub use super::functions::aggregate_ordering::*; #[doc(inline)] pub use super::functions::aggregate_folding::*; #[doc(inline)] pub use super::sql_literal::sql; + #[doc(inline)] pub use super::exists::exists; #[cfg(feature = "postgres")] pub use pg::expression::dsl::*;