From 0c52e53565247c02a1c1ce66ca4a9f626a85aa2e Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Thu, 8 Dec 2016 12:26:10 -0500 Subject: [PATCH] Add a function for SQL `EXISTS` expressions. While this is useful for some cases where you don't want to load the rows, this won't fill every use case for the expression, as right now you wouldn't be able to build a query that references the outer table. For us to do that and have it be type safe we'd need overlapping impls for `SelectableExpression` (story of my life), which requires https://github.com/rust-lang/rust/issues/29864 being implemented. Fixes #414. --- CHANGELOG.md | 5 ++ diesel/src/expression/exists.rs | 81 +++++++++++++++++++++++++++++++++ diesel/src/expression/mod.rs | 3 ++ 3 files changed, 89 insertions(+) create mode 100644 diesel/src/expression/exists.rs 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::*;