-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add sqlite functions json
and jsonb
#4388
Changes from 2 commits
61c8c38
287c9f6
75ccf87
d381824
0991d42
683a8af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the correct signature here would be |
||
} | ||
|
||
#[cfg(feature = "sqlite")] | ||
define_sql_function! { | ||
/// | ||
weiznich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// # 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the correct signature here would be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you want to say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that should be |
||
} |
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>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer not to enable the bundled feature here as that breaks anyone that requires dynamically linking libsqlite3.
I would rather prefer to have a
SELECT sqlite_version();
query in the doc tests and just skip the test if the local version is too old.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, this has nothing to do with the version of
sqlite3
, but with the feature switch of thelibsqlite3-sys
crate thatdiesel
depends on. If we continue to use thebundled_bindings
feature instead ofbundled
, even if the sqlite3 version is higher than 3.45.0 (mine is 3.47.2), it will still report an error ofno such function: jsonb
. This means that we cannot testjsonb
at all, and (possibly) all JSONB-related functions mentioned in that issue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have raised an issue under
rusqlite
(the repository wherelibsqlite3-sys
is located) and received a relevant answer. rusqlite/rusqlite#1614There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested that locally and I believe it really just depends on which libsqlite3 (system library, not rust crate) version is linked. If that version is newer than 3.45 it works, if it's older it doesn't work. At least that's the case with my local 3.46 version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh you are right, I rechecked my libsqlite and found that it was linked to the system library provided by MacOS instead of the library provided by homebrew. 😭