diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eb3445e99..17bfd6366a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All user visible changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/), as described for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) +## Unreleased + +### Added + +* Added a function which maps to SQL `NOT`. See [the docs][not-0.14.0] for more + details. + +[not-0.14.0]: http://docs.diesel.rs/diesel/expression/dsl/fn.not.html + ## [0.13.0] - 2017-05-15 ### Added diff --git a/diesel/src/expression/helper_types.rs b/diesel/src/expression/helper_types.rs index 9cc268cd15..6c5228c01d 100644 --- a/diesel/src/expression/helper_types.rs +++ b/diesel/src/expression/helper_types.rs @@ -35,6 +35,8 @@ pub type Between = super::predicates::Between, AsExpr>>; pub type NotBetween = super::predicates::NotBetween, AsExpr>>; +/// The return type of `not(expr)` +pub type Not = super::not::Not>; #[doc(inline)] pub use super::predicates::{IsNull, IsNotNull, Asc, Desc}; diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index 69f027f2a7..9966e23725 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -34,6 +34,7 @@ pub mod functions; pub mod grouped; #[macro_use] pub mod helper_types; +mod not; #[doc(hidden)] pub mod nullable; #[doc(hidden)] @@ -47,11 +48,12 @@ mod unchecked_bind; /// in functions where you need them. pub mod dsl { #[doc(inline)] pub use super::count::{count, count_star}; - #[doc(inline)] pub use super::functions::date_and_time::*; - #[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; + #[doc(inline)] pub use super::functions::aggregate_folding::*; + #[doc(inline)] pub use super::functions::aggregate_ordering::*; + #[doc(inline)] pub use super::functions::date_and_time::*; + #[doc(inline)] pub use super::not::not; + #[doc(inline)] pub use super::sql_literal::sql; #[cfg(feature = "postgres")] pub use pg::expression::dsl::*; diff --git a/diesel/src/expression/not.rs b/diesel/src/expression/not.rs new file mode 100644 index 0000000000..9bb3f738ee --- /dev/null +++ b/diesel/src/expression/not.rs @@ -0,0 +1,72 @@ +use expression::*; +use query_builder::*; +use result::QueryResult; +use types::Bool; + +/// Creates a SQL `NOT` expression +/// +/// # Example +/// +/// ```rust +/// # #[macro_use] extern crate diesel; +/// # include!("src/doctest_setup.rs"); +/// # +/// # table! { +/// # users { +/// # id -> Integer, +/// # name -> VarChar, +/// # } +/// # } +/// # +/// # fn main() { +/// # use self::users::dsl::*; +/// # let connection = establish_connection(); +/// use diesel::expression::not; +/// +/// let users_with_name = users.select(id).filter(name.eq("Sean")); +/// let users_not_with_name = users.select(id).filter( +/// not(name.eq("Sean"))); +/// +/// assert_eq!(Ok(1), users_with_name.first(&connection)); +/// assert_eq!(Ok(2), users_not_with_name.first(&connection)); +/// # } +/// ``` +pub fn not>(expr: T) -> Not { + Not(expr.as_expression()) +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct Not(T); + +impl> Expression for Not { + type SqlType = Bool; +} + +impl AppearsOnTable for Not where + T: AppearsOnTable, + Not: Expression, +{ +} + +impl SelectableExpression for Not where + T: SelectableExpression, + Not: AppearsOnTable, +{ +} + +impl NonAggregate for Not {} + +impl QueryFragment for Not where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("NOT ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl_query_id!(Not); diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs index f7c57132e1..74ee70729b 100644 --- a/diesel_tests/tests/filter.rs +++ b/diesel_tests/tests/filter.rs @@ -268,6 +268,33 @@ fn or_doesnt_mess_with_precidence_of_previous_statements() { assert_eq!(Ok(0), count); } +#[test] +fn not_does_not_affect_expressions_other_than_those_passed_to_it() { + use schema::users::dsl::*; + use diesel::expression::dsl::not; + + let connection = connection_with_sean_and_tess_in_users_table(); + let count = users.filter(not(name.eq("Tess"))) + .filter(id.eq(1)) + .count() + .get_result(&connection); + + assert_eq!(Ok(1), count); +} + +#[test] +fn not_affects_arguments_passed_when_they_contain_higher_operator_precedence() { + use schema::users::dsl::*; + use diesel::expression::dsl::not; + + let connection = connection_with_sean_and_tess_in_users_table(); + let count = users.filter(not(name.eq("Tess").and(id.eq(1)))) + .count() + .get_result(&connection); + + assert_eq!(Ok(2), count); +} + use diesel::types::VarChar; sql_function!(lower, lower_t, (x: VarChar) -> VarChar);