Skip to content

Commit 0c52e53

Browse files
committed
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 rust-lang/rust#29864 being implemented. Fixes #414.
1 parent 47d1196 commit 0c52e53

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
3939

4040
[insert]: http://docs.diesel.rs/diesel/fn.insert.html
4141

42+
* Added a function for SQL `EXISTS` expressions. See
43+
[`diesel::expression::dsl::exists`][exists] for details.
44+
45+
[exists]: http://docs.diesel.rs/diesel/expression/dsl/fn.sql.html
46+
4247
### Changed
4348

4449
* All macros with the same name as traits we can derive (e.g. `Queryable!`) have

diesel/src/expression/exists.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use backend::Backend;
2+
use expression::{Expression, SelectableExpression, NonAggregate};
3+
use query_builder::*;
4+
use result::QueryResult;
5+
use types::Bool;
6+
7+
/// Creates a SQL `EXISTS` expression.
8+
///
9+
/// The argument must be a complete SQL query. The result of this could in
10+
/// theory be passed to `.filter`, but since the query cannot reference columns
11+
/// from the outer query, this is of limited usefulness.
12+
///
13+
/// # Example
14+
///
15+
/// ```rust
16+
/// # #[macro_use] extern crate diesel;
17+
/// # include!("src/doctest_setup.rs");
18+
/// #
19+
/// # table! {
20+
/// # users {
21+
/// # id -> Integer,
22+
/// # name -> VarChar,
23+
/// # }
24+
/// # }
25+
/// #
26+
/// # fn main() {
27+
/// # use self::users::dsl::*;
28+
/// # use diesel::select;
29+
/// # use diesel::expression::dsl::exists;
30+
/// # let connection = establish_connection();
31+
/// let sean_exists = select(exists(users.filter(name.eq("Sean"))))
32+
/// .get_result(&connection);
33+
/// let jim_exists = select(exists(users.filter(name.eq("Jim"))))
34+
/// .get_result(&connection);
35+
/// assert_eq!(Ok(true), sean_exists);
36+
/// assert_eq!(Ok(false), jim_exists);
37+
/// # }
38+
/// ```
39+
pub fn exists<T: AsQuery>(query: T) -> Exists<T::Query> {
40+
Exists(query.as_query())
41+
}
42+
43+
#[derive(Debug, Clone, Copy)]
44+
pub struct Exists<T>(T);
45+
46+
impl<T> Expression for Exists<T> where
47+
T: Query,
48+
{
49+
type SqlType = Bool;
50+
}
51+
52+
impl<T, QS> SelectableExpression<QS> for Exists<T> where
53+
Exists<T>: Expression,
54+
{
55+
}
56+
57+
impl<T> NonAggregate for Exists<T> {
58+
}
59+
60+
impl<T, DB> QueryFragment<DB> for Exists<T> where
61+
DB: Backend,
62+
T: QueryFragment<DB>,
63+
{
64+
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
65+
out.push_sql("EXISTS (");
66+
try!(self.0.to_sql(out));
67+
out.push_sql(")");
68+
Ok(())
69+
}
70+
71+
fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> {
72+
try!(self.0.collect_binds(out));
73+
Ok(())
74+
}
75+
76+
fn is_safe_to_cache_prepared(&self) -> bool {
77+
self.0.is_safe_to_cache_prepared()
78+
}
79+
}
80+
81+
impl_query_id!(Exists<T>);

diesel/src/expression/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub mod array_comparison;
2525
pub mod bound;
2626
#[doc(hidden)]
2727
pub mod count;
28+
#[doc(hidden)]
29+
pub mod exists;
2830
pub mod expression_methods;
2931
#[doc(hidden)]
3032
pub mod functions;
@@ -47,6 +49,7 @@ pub mod dsl {
4749
#[doc(inline)] pub use super::functions::aggregate_ordering::*;
4850
#[doc(inline)] pub use super::functions::aggregate_folding::*;
4951
#[doc(inline)] pub use super::sql_literal::sql;
52+
#[doc(inline)] pub use super::exists::exists;
5053

5154
#[cfg(feature = "postgres")]
5255
pub use pg::expression::dsl::*;

0 commit comments

Comments
 (0)