Skip to main content

diesel/sqlite/expression/
expression_methods.rs

1//! Sqlite specific expression methods.
2
3pub(in crate::sqlite) use self::private::{
4    BinaryOrNullableBinary, JsonOrNullableJson, MaybeNullableValue, NotBlob, TextOrNullableText,
5    TextOrNullableTextOrBinaryOrNullableBinary,
6};
7use super::operators::*;
8use crate::dsl;
9use crate::expression::grouped::Grouped;
10use crate::expression::{AsExpression, Expression};
11use crate::expression_methods::json_expression_methods::private::JsonOrNullableJsonOrJsonbOrNullableJsonb;
12use crate::expression_methods::json_expression_methods::{AnyJsonExpressionMethods, JsonIndex};
13use crate::sql_types::SqlType;
14
15/// Sqlite specific methods which are present on all expressions.
16#[cfg(feature = "__sqlite-shared")]
17pub trait SqliteExpressionMethods: Expression + Sized {
18    /// Creates a Sqlite `IS` expression.
19    ///
20    /// The `IS` operator work like = except when one or both of the operands are NULL.
21    /// In this case, if both operands are NULL, then the `IS` operator evaluates to true.
22    /// If one operand is NULL and the other is not, then the `IS` operator evaluates to false.
23    /// It is not possible for an `IS` expression to evaluate to NULL.
24    ///
25    /// # Example
26    ///
27    /// ```rust
28    /// # include!("../../doctest_setup.rs");
29    /// #
30    /// # // Reading the query result requires reading memory allocated by the
31    /// # // native sqlite library, which miri cannot track provenance for.
32    /// # #[cfg(miri)]
33    /// # fn main() {}
34    /// # #[cfg(not(miri))]
35    /// # fn main() {
36    /// #     run_test().unwrap();
37    /// # }
38    /// #
39    /// # fn run_test() -> QueryResult<()> {
40    /// #     use schema::animals::dsl::*;
41    /// #     let connection = &mut establish_connection();
42    /// let jack_is_a_dog = animals
43    ///     .select(name)
44    ///     .filter(species.is("dog"))
45    ///     .get_results::<Option<String>>(connection)?;
46    /// assert_eq!(vec![Some("Jack".to_string())], jack_is_a_dog);
47    /// #     Ok(())
48    /// # }
49    /// ```
50    fn is<T>(self, other: T) -> dsl::Is<Self, T>
51    where
52        Self::SqlType: SqlType,
53        T: AsExpression<Self::SqlType>,
54    {
55        Grouped(Is::new(self, other.as_expression()))
56    }
57
58    /// Creates a Sqlite `IS NOT` expression.
59    ///
60    /// The `IS NOT` operator work like != except when one or both of the operands are NULL.
61    /// In this case, if both operands are NULL, then the `IS NOT` operator evaluates to false.
62    /// If one operand is NULL and the other is not, then the `IS NOT` operator is true.
63    /// It is not possible for an `IS NOT` expression to evaluate to NULL.
64    ///
65    /// # Example
66    ///
67    /// ```rust
68    /// # include!("../../doctest_setup.rs");
69    /// #
70    /// # // Reading the query result requires reading memory allocated by the
71    /// # // native sqlite library, which miri cannot track provenance for.
72    /// # #[cfg(miri)]
73    /// # fn main() {}
74    /// # #[cfg(not(miri))]
75    /// # fn main() {
76    /// #     run_test().unwrap();
77    /// # }
78    /// #
79    /// # fn run_test() -> QueryResult<()> {
80    /// #     use schema::animals::dsl::*;
81    /// #     let connection = &mut establish_connection();
82    /// let jack_is_not_a_spider = animals
83    ///     .select(name)
84    ///     .filter(species.is_not("spider"))
85    ///     .get_results::<Option<String>>(connection)?;
86    /// assert_eq!(vec![Some("Jack".to_string())], jack_is_not_a_spider);
87    /// #     Ok(())
88    /// # }
89    /// ```
90    #[allow(clippy::wrong_self_convention)] // This is named after the sql operator
91    fn is_not<T>(self, other: T) -> dsl::IsNot<Self, T>
92    where
93        Self::SqlType: SqlType,
94        T: AsExpression<Self::SqlType>,
95    {
96        Grouped(IsNot::new(self, other.as_expression()))
97    }
98}
99
100impl<T: Expression> SqliteExpressionMethods for T {}
101
102/// SQLite specific methods present on JSON and JSONB expressions.
103#[cfg(feature = "__sqlite-shared")]
104pub trait SqliteAnyJsonExpressionMethods: AnyJsonExpressionMethods + Expression + Sized {
105    /// Creates a SQLite `->` expression.
106    ///
107    /// This operator extracts the value associated with the given path or key from a JSON value.
108    /// The right-hand side can be:
109    /// - A string path expression (e.g., `"$.key"`, `"$.c"`, or `"c"` which is interpreted as `"$.c"`)
110    /// - An integer for array indexing (e.g., `0` for the first element, or `-1` for the last element on SQLite 3.47+)
111    ///
112    /// **Always returns a TEXT JSON representation** (SQL type `Json`), even when the input is JSONB.
113    /// To get JSONB output, use `jsonb_extract()` function instead.
114    ///
115    /// # Example
116    ///
117    /// ```rust
118    /// # include!("../../doctest_setup.rs");
119    /// #
120    /// # table! {
121    /// #    contacts {
122    /// #        id -> Integer,
123    /// #        name -> Text,
124    /// #        address -> Json,
125    /// #    }
126    /// # }
127    /// #
128    /// # // Reading the query result requires reading memory allocated by the
129    /// # // native sqlite library, which miri cannot track provenance for.
130    /// # #[cfg(miri)]
131    /// # fn main() {}
132    /// # #[cfg(not(miri))]
133    /// # fn main() {
134    /// #     #[cfg(feature = "serde_json")]
135    /// #     run_test().unwrap();
136    /// # }
137    /// #
138    /// # #[cfg(feature = "serde_json")]
139    /// # fn run_test() -> QueryResult<()> {
140    /// #     use self::contacts::dsl::*;
141    /// #     use diesel::dsl::sql;
142    /// #     use diesel::sql_types::Json;
143    /// #     let conn = &mut establish_connection();
144    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
145    /// #     diesel::sql_query("CREATE TABLE contacts (
146    /// #         id INTEGER PRIMARY KEY,
147    /// #         name TEXT NOT NULL,
148    /// #         address TEXT NOT NULL
149    /// #     )").execute(conn).unwrap();
150    /// #
151    /// let json_value = serde_json::json!({
152    ///     "street": "Article Circle Expressway 1",
153    ///     "city": "North Pole",
154    ///     "postcode": "99705",
155    ///     "state": "Alaska"
156    /// });
157    ///
158    /// let result = diesel::select(sql::<Json>(r#"json('{"a": {"b": [1, 2, 3]}}')"#)
159    ///     .retrieve_as_object_sqlite("$.a.b[0]"))
160    ///     .get_result::<serde_json::Value>(conn)?;
161    /// assert_eq!(serde_json::json!(1), result);
162    ///
163    /// let result = diesel::select(sql::<Jsonb>(r#"json('{"a": [1, 2, 3]}')"#)
164    ///     .retrieve_as_object_sqlite("$.a[1]"))
165    ///     .get_result::<serde_json::Value>(conn)?;
166    /// assert_eq!(serde_json::json!(2), result);
167    ///
168    /// #     Ok(())
169    /// # }
170    /// ```
171    fn retrieve_as_object_sqlite<T>(
172        self,
173        other: T,
174    ) -> crate::sqlite::expression::helper_types::RetrieveAsObjectSqlite<Self, T>
175    where
176        T: JsonIndex,
177        <T::Expression as Expression>::SqlType: SqlType,
178    {
179        Grouped(RetrieveAsObjectSqlite::new(
180            self,
181            other.into_json_index_expression(),
182        ))
183    }
184}
185
186#[doc(hidden)]
187impl<T> SqliteAnyJsonExpressionMethods for T
188where
189    T: Expression,
190    T::SqlType: JsonOrNullableJsonOrJsonbOrNullableJsonb,
191{
192}
193
194pub(in crate::sqlite) mod private {
195    use crate::sql_types::{
196        BigInt, Binary, Bool, Date, Double, Float, Integer, Json, MaybeNullableType, Nullable,
197        Numeric, SingleValue, SmallInt, SqlType, Text, Time, Timestamp, TimestamptzSqlite,
198    };
199
200    #[diagnostic::on_unimplemented(
201        message = "`{Self}` is neither `diesel::sql_types::Text` nor `diesel::sql_types::Nullable<Text>`",
202        note = "try to provide an expression that produces one of the expected sql types"
203    )]
204    pub trait TextOrNullableText {}
205
206    impl TextOrNullableText for Text {}
207    impl TextOrNullableText for Nullable<Text> {}
208
209    #[diagnostic::on_unimplemented(
210        message = "`{Self}` is neither `diesel::sql_types::Binary` nor `diesel::sql_types::Nullable<Binary>`",
211        note = "try to provide an expression that produces one of the expected sql types"
212    )]
213    pub trait BinaryOrNullableBinary {}
214
215    impl BinaryOrNullableBinary for Binary {}
216    impl BinaryOrNullableBinary for Nullable<Binary> {}
217
218    #[diagnostic::on_unimplemented(
219        message = "`{Self}` is neither `diesel::sql_types::Text`, `diesel::sql_types::Nullable<Text>`, `diesel::sql_types::Binary` nor `diesel::sql_types::Nullable<Binary>`",
220        note = "try to provide an expression that produces one of the expected sql types"
221    )]
222    pub trait TextOrNullableTextOrBinaryOrNullableBinary {}
223
224    impl TextOrNullableTextOrBinaryOrNullableBinary for Text {}
225    impl TextOrNullableTextOrBinaryOrNullableBinary for Nullable<Text> {}
226    impl TextOrNullableTextOrBinaryOrNullableBinary for Binary {}
227    impl TextOrNullableTextOrBinaryOrNullableBinary for Nullable<Binary> {}
228
229    #[diagnostic::on_unimplemented(
230        message = "`{Self}` is neither `diesel::sql_types::Json` nor `diesel::sql_types::Nullable<Json>`",
231        note = "try to provide an expression that produces one of the expected sql types"
232    )]
233    pub trait JsonOrNullableJson {}
234    impl JsonOrNullableJson for Json {}
235    impl JsonOrNullableJson for Nullable<Json> {}
236
237    pub trait MaybeNullableValue<T>: SingleValue {
238        type Out: SingleValue;
239    }
240
241    impl<T, O> MaybeNullableValue<O> for T
242    where
243        T: SingleValue,
244        T::IsNull: MaybeNullableType<O>,
245        <T::IsNull as MaybeNullableType<O>>::Out: SingleValue,
246    {
247        type Out = <T::IsNull as MaybeNullableType<O>>::Out;
248    }
249
250    #[diagnostic::on_unimplemented(
251        message = "`{Self}` is neither any of `diesel::sql_types::{{
252            Text, Float, Double, Numeric,  Bool, Integer, SmallInt, BigInt,
253            Date, Time, Timestamp, TimestamptzSqlite, Json
254         }}`  nor `diesel::sql_types::Nullable<Any of the above>`",
255        note = "try to provide an expression that produces one of the expected sql types"
256    )]
257    pub trait NotBlob: SqlType + SingleValue {}
258
259    impl<T> NotBlob for Nullable<T> where T: NotBlob {}
260    impl NotBlob for Text {}
261    impl NotBlob for Float {}
262    impl NotBlob for Double {}
263    impl NotBlob for Numeric {}
264    impl NotBlob for Bool {}
265    impl NotBlob for Integer {}
266    impl NotBlob for SmallInt {}
267    impl NotBlob for BigInt {}
268    impl NotBlob for Date {}
269    impl NotBlob for Time {}
270    impl NotBlob for Timestamp {}
271    impl NotBlob for TimestamptzSqlite {}
272    impl NotBlob for Json {}
273}