diesel/expression_methods/bool_expression_methods.rs
1use crate::dsl;
2use crate::expression::grouped::Grouped;
3use crate::expression::operators::{And, Or};
4use crate::expression::{AsExpression, Expression, TypedExpressionType};
5use crate::sql_types::{self, BoolOrNullableBool, SqlType};
6
7/// Methods present on boolean expressions
8pub trait BoolExpressionMethods: Expression + Sized {
9 /// Creates a SQL `AND` expression
10 ///
11 /// # Example
12 ///
13 /// ```
14 /// # include!("../doctest_setup.rs");
15 /// #
16 /// # // Reading the query result requires reading memory allocated by the
17 /// # // native sqlite library, which miri cannot track provenance for.
18 /// # #[cfg(miri)]
19 /// # fn main() {}
20 /// # #[cfg(not(miri))]
21 /// # fn main() {
22 /// # run_test().unwrap();
23 /// # }
24 /// #
25 /// # fn run_test() -> QueryResult<()> {
26 /// # use schema::animals::dsl::*;
27 /// # let connection = &mut establish_connection();
28 /// #
29 /// diesel::insert_into(animals)
30 /// .values(&vec![
31 /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")),
32 /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")),
33 /// ])
34 /// .execute(connection)?;
35 ///
36 /// let data = animals
37 /// .select((species, name))
38 /// .filter(species.eq("ferret").and(name.eq("Jack")))
39 /// .load(connection)?;
40 /// let expected = vec![(String::from("ferret"), Some(String::from("Jack")))];
41 /// assert_eq!(expected, data);
42 /// # Ok(())
43 /// # }
44 /// ```
45 fn and<T, ST>(self, other: T) -> dsl::And<Self, T, ST>
46 where
47 Self::SqlType: SqlType,
48 ST: SqlType + TypedExpressionType + BoolOrNullableBool,
49 T: AsExpression<ST>,
50 And<Self, T::Expression>: Expression,
51 {
52 Grouped(And::new(self, other.as_expression()))
53 }
54
55 /// Creates a SQL `OR` expression
56 ///
57 /// The result will be wrapped in parenthesis, so that precedence matches
58 /// that of your function calls. For example, `false.and(false.or(true))`
59 /// will generate the SQL `FALSE AND (FALSE OR TRUE)`, which returns `false`
60 ///
61 /// # Example
62 ///
63 /// ```
64 /// # include!("../doctest_setup.rs");
65 /// #
66 /// # // Reading the query result requires reading memory allocated by the
67 /// # // native sqlite library, which miri cannot track provenance for.
68 /// # #[cfg(miri)]
69 /// # fn main() {}
70 /// # #[cfg(not(miri))]
71 /// # fn main() {
72 /// # run_test().unwrap();
73 /// # }
74 /// #
75 /// # fn run_test() -> QueryResult<()> {
76 /// # use schema::animals::dsl::*;
77 /// # let connection = &mut establish_connection();
78 /// #
79 /// diesel::insert_into(animals)
80 /// .values(&vec![
81 /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")),
82 /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")),
83 /// ])
84 /// .execute(connection)?;
85 ///
86 /// let data = animals
87 /// .select((species, name))
88 /// .filter(species.eq("ferret").or(name.eq("Jack")))
89 /// .load(connection)?;
90 /// let expected = vec![
91 /// (String::from("dog"), Some(String::from("Jack"))),
92 /// (String::from("ferret"), Some(String::from("Freddy"))),
93 /// (String::from("ferret"), Some(String::from("Jack"))),
94 /// ];
95 /// assert_eq!(expected, data);
96 /// # Ok(())
97 /// # }
98 /// ```
99 fn or<T, ST>(self, other: T) -> dsl::Or<Self, T, ST>
100 where
101 Self::SqlType: SqlType,
102 ST: SqlType + TypedExpressionType + BoolOrNullableBool,
103 T: AsExpression<ST>,
104 Or<Self, T::Expression>: Expression,
105 {
106 Grouped(Or::new(self, other.as_expression()))
107 }
108}
109
110impl<T> BoolExpressionMethods for T
111where
112 T: Expression,
113 T::SqlType: BoolOrNullableBool,
114{
115}
116
117/// Allow ~type inference on [And](crate::helper_types::And) and [Or](crate::helper_types::Or)
118/// helper types
119///
120/// This is used to be statistically correct as last generic parameter of `dsl::And` and `dsl::Or`
121/// without having to specify an additional type parameter.
122///
123/// It works with types that are [Expression]s and have a [`SqlType`](Expression::SqlType) that is
124/// either [`Bool`](sql_types::Bool) or [`Nullable<Bool>`](sql_types::Nullable), and with [`bool`]
125/// (and `Option<bool>` and references to those).
126///
127/// Cases where an additional type parameter would still have to be specified in the helper type
128/// generic parameters are:
129/// - If this trait isn't implemented for the `other` parameter of the expression
130/// (in that case the user (you?) probably wants to implement it)
131/// - If the user actually was using the not-preferred implementation of `AsExpression`
132/// (e.g. towards `Nullable<Bool>` instead of `Bool`)
133pub trait PreferredBoolSqlType {
134 /// The preferred `Bool` SQL type for this AsExpression implementation.
135 ///
136 /// That should be either `Bool` or `Nullable<Bool>`.
137 type PreferredSqlType;
138}
139
140impl<E: Expression> PreferredBoolSqlType for E
141where
142 E::SqlType: BoolOrNullableBool,
143{
144 type PreferredSqlType = <E as Expression>::SqlType;
145}
146
147/// This impl has to live in Diesel because otherwise it would conflict with the blanket impl above
148/// because "diesel might add an implementation of Expression for bool"
149impl PreferredBoolSqlType for bool {
150 type PreferredSqlType = sql_types::Bool;
151}
152
153impl PreferredBoolSqlType for &bool {
154 type PreferredSqlType = sql_types::Bool;
155}
156
157impl PreferredBoolSqlType for &&bool {
158 type PreferredSqlType = sql_types::Bool;
159}
160
161impl PreferredBoolSqlType for Option<bool> {
162 type PreferredSqlType = sql_types::Nullable<sql_types::Bool>;
163}
164
165impl PreferredBoolSqlType for &Option<bool> {
166 type PreferredSqlType = sql_types::Nullable<sql_types::Bool>;
167}
168
169impl PreferredBoolSqlType for &&Option<bool> {
170 type PreferredSqlType = sql_types::Nullable<sql_types::Bool>;
171}