Skip to content

Commit

Permalink
add SQLite functions json and jsonb
Browse files Browse the repository at this point in the history
  • Loading branch information
xuehaonan27 committed Dec 11, 2024
1 parent bfb16f1 commit 61c8c38
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 2 deletions.
3 changes: 3 additions & 0 deletions diesel/src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ pub(crate) mod dsl {
#[cfg(feature = "postgres_backend")]
pub use crate::pg::expression::dsl::*;

#[cfg(feature = "sqlite")]
pub use crate::sqlite::expression::dsl::*;

/// The return type of [`count(expr)`](crate::dsl::count())
pub type count<Expr> = super::count::count<SqlTypeOf<Expr>, Expr>;

Expand Down
26 changes: 26 additions & 0 deletions diesel/src/sqlite/expression/expression_methods.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Sqlite specific expression methods.
pub(in crate::sqlite) use self::private::{
JsonOrNullableJsonOrJsonbOrNullableJsonb, MaybeNullableValue,
};
use super::operators::*;
use crate::dsl;
use crate::expression::grouped::Grouped;
Expand Down Expand Up @@ -82,3 +85,26 @@ pub trait SqliteExpressionMethods: Expression + Sized {
}

impl<T: Expression> SqliteExpressionMethods for T {}

pub(in crate::sqlite) mod private {
use crate::sql_types::{Json, Jsonb, MaybeNullableType, Nullable, SingleValue};

pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}

pub trait MaybeNullableValue<T>: SingleValue {
type Out: SingleValue;
}

impl<T, O> MaybeNullableValue<O> for T
where
T: SingleValue,
T::IsNull: MaybeNullableType<O>,
<T::IsNull as MaybeNullableType<O>>::Out: SingleValue,
{
type Out = <T::IsNull as MaybeNullableType<O>>::Out;
}
}
114 changes: 114 additions & 0 deletions diesel/src/sqlite/expression/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//! SQLite specific functions
use crate::expression::functions::define_sql_function;
use crate::sql_types::*;
use crate::sqlite::expression::expression_methods::JsonOrNullableJsonOrJsonbOrNullableJsonb;
use crate::sqlite::expression::expression_methods::MaybeNullableValue;

#[cfg(feature = "sqlite")]
define_sql_function! {
/// Verifies that its argument is a valid JSON string or JSONB blob and returns a minified
/// version of that JSON string with all unnecessary whitespace removed.
///
/// # Example
///
/// ```rust
/// # include!("../../doctest_setup.rs");
/// #
/// # fn main() {
/// # #[cfg(feature = "serde_json")]
/// # run_test().unwrap();
/// # }
/// #
/// # #[cfg(feature = "serde_json")]
/// # fn run_test() -> QueryResult<()> {
/// # use diesel::dsl::json;
/// # use serde_json::{json, Value};
/// # use diesel::sql_types::{Text, Json, Jsonb, Nullable};
/// # let connection = &mut establish_connection();
///
/// let result = diesel::select(json::<Json, _>(json!({"a": "b", "c": 1})))
/// .get_result::<String>(connection)?;
///
/// assert_eq!(r#"{"a":"b","c":1}"#, result);
///
/// let result = diesel::select(json::<Json, _>(json!({ "this" : "is", "a": [ "test" ] })))
/// .get_result::<String>(connection)?;
///
/// assert_eq!(r#"{"a":["test"],"this":"is"}"#, result);
///
/// let result = diesel::select(json::<Nullable<Json>, _>(None::<Value>))
/// .get_result::<Option<String>>(connection)?;
///
/// assert!(result.is_none());
///
/// let result = diesel::select(json::<Jsonb, _>(json!({"a": "b", "c": 1})))
/// .get_result::<String>(connection)?;
///
/// assert_eq!(r#"{"a":"b","c":1}"#, result);
///
/// let result = diesel::select(json::<Jsonb, _>(json!({ "this" : "is", "a": [ "test" ] })))
/// .get_result::<String>(connection)?;
///
/// assert_eq!(r#"{"a":["test"],"this":"is"}"#, result);
///
/// let result = diesel::select(json::<Nullable<Jsonb>, _>(None::<Value>))
/// .get_result::<Option<String>>(connection)?;
///
/// assert!(result.is_none());
///
///
/// # Ok(())
/// # }
/// ```
fn json<E: JsonOrNullableJsonOrJsonbOrNullableJsonb + SingleValue + MaybeNullableValue<Text>>(e: E) -> E::Out;
}

#[cfg(feature = "sqlite")]
define_sql_function! {
///
/// # Example
///
/// ```rust
/// # include!("../../doctest_setup.rs");
/// #
/// # fn main() {
/// # #[cfg(feature = "serde_json")]
/// # run_test().unwrap();
/// # }
/// #
/// # #[cfg(feature = "serde_json")]
/// # fn run_test() -> QueryResult<()> {
/// # use diesel::dsl::jsonb;
/// # use serde_json::{json, Value};
/// # use diesel::sql_types::{Text, Json, Jsonb, Nullable};
/// # let connection = &mut establish_connection();
///
/// let result = diesel::select(jsonb::<Json, _>(json!({"a": "b", "c": 1})))
/// .get_result::<Value>(connection)?;
///
/// assert_eq!(json!({"a": "b", "c": 1}), result);
/// println!("json abc1");
///
/// let result = diesel::select(jsonb::<Jsonb, _>(json!({"a": "b", "c": 1})))
/// .get_result::<Value>(connection)?;
///
/// assert_eq!(json!({"a": "b", "c": 1}), result);
/// println!("jsonb abc1");
///
/// let result = diesel::select(jsonb::<Nullable<Json>, _>(None::<Value>))
/// .get_result::<Option<Value>>(connection)?;
///
/// assert!(result.is_none());
/// println!("json null");
///
/// let result = diesel::select(jsonb::<Nullable<Jsonb>, _>(None::<Value>))
/// .get_result::<Option<Value>>(connection)?;
///
/// assert!(result.is_none());
/// println!("jsonb null");
///
/// # Ok(())
/// # }
/// ```
fn jsonb<E: JsonOrNullableJsonOrJsonbOrNullableJsonb + SingleValue + MaybeNullableValue<Jsonb>>(e: E) -> E::Out;
}
12 changes: 11 additions & 1 deletion diesel/src/sqlite/expression/helper_types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
use crate::dsl::AsExpr;
use crate::dsl::{AsExpr, SqlTypeOf};
use crate::expression::grouped::Grouped;

/// The return type of `lhs.is(rhs)`.
pub type Is<Lhs, Rhs> = Grouped<super::operators::Is<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of `lhs.is_not(rhs)`.
pub type IsNot<Lhs, Rhs> = Grouped<super::operators::IsNot<Lhs, AsExpr<Rhs, Lhs>>>;

/// Return type of [`json(json)`](super::functions::json())
#[allow(non_camel_case_types)]
#[cfg(feature = "sqlite")]
pub type json<E> = super::functions::json<SqlTypeOf<E>, E>;

/// Return type of [`jsonb(json)`](super::functions::jsonb())
#[allow(non_camel_case_types)]
#[cfg(feature = "sqlite")]
pub type jsonb<E> = super::functions::jsonb<SqlTypeOf<E>, E>;
11 changes: 11 additions & 0 deletions diesel/src/sqlite/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,16 @@
//! kept separate purely for documentation purposes.
pub(crate) mod expression_methods;
pub mod functions;
pub(crate) mod helper_types;
mod operators;

/// SQLite specific expression DSL methods.
///
/// This module will be glob imported by
/// [`diesel::dsl`](crate::dsl) when compiled with the `feature =
/// "postgres"` flag.
pub mod dsl {
#[doc(inline)]
pub use super::functions::*;
}
2 changes: 1 addition & 1 deletion diesel/src/sqlite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
pub(crate) mod backend;
mod connection;
pub(crate) mod expression;
pub mod expression;

pub mod query_builder;

Expand Down
22 changes: 22 additions & 0 deletions diesel_derives/tests/auto_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ table! {
}
}

#[cfg(feature = "sqlite")]
table! {
sqlite_extras {
id -> Integer,
text -> Text,
json -> Json,
jsonb -> Jsonb,
}
}

joinable!(posts -> users(user_id));
joinable!(posts2 -> users(user_id));
joinable!(posts3 -> users(user_id));
Expand Down Expand Up @@ -465,6 +475,18 @@ fn postgres_functions() -> _ {
)
}

#[cfg(feature = "sqlite")]
#[auto_type]
fn sqlite_functions() -> _ {
use diesel::sqlite::expression::functions::{json, jsonb};
(
json(sqlite_extras::json),
json(sqlite_extras::jsonb),
jsonb(sqlite_extras::json),
jsonb(sqlite_extras::jsonb),
)
}

#[auto_type]
fn with_lifetime<'a>(name: &'a str) -> _ {
users::table.filter(users::name.eq(name))
Expand Down

0 comments on commit 61c8c38

Please sign in to comment.