From 48f4b6c7c5f57b9debe80bad0ce905c9e62b0a9e Mon Sep 17 00:00:00 2001 From: Ananth Prathap <22am014@sctce.ac.in> Date: Sun, 2 Mar 2025 04:51:04 +0530 Subject: [PATCH 1/3] feat(sqlite): Add `json_type` and `json_type_with_path` functions --- diesel/src/sqlite/expression/functions.rs | 142 +++++++++++++++++++ diesel/src/sqlite/expression/helper_types.rs | 11 ++ diesel_derives/tests/auto_type.rs | 2 + 3 files changed, 155 insertions(+) diff --git a/diesel/src/sqlite/expression/functions.rs b/diesel/src/sqlite/expression/functions.rs index ff78163e628c..b8c60ce53cdf 100644 --- a/diesel/src/sqlite/expression/functions.rs +++ b/diesel/src/sqlite/expression/functions.rs @@ -640,4 +640,146 @@ extern "SQL" { #[sql_name = "json_valid"] #[cfg(feature = "sqlite")] fn json_valid>(j: J) -> J::Out; + + /// The json_type(X) function returns the "type" of the outermost element of X. + /// The "type" returned by json_type() is one of the following SQL text values: + /// 'null', 'true', 'false', 'integer', 'real', 'text', 'array', or 'object'. + /// + /// # 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::{sql, json_type}; + /// # use serde_json::{json, Value}; + /// # use diesel::sql_types::{Text, Json, Jsonb, Nullable}; + /// # let connection = &mut establish_connection(); + /// + /// let version = diesel::select(sql::("sqlite_version();")) + /// .get_result::(connection)?; + /// + /// // Querying SQLite version should not fail. + /// let version_components: Vec<&str> = version.split('.').collect(); + /// let major: u32 = version_components[0].parse().unwrap(); + /// let minor: u32 = version_components[1].parse().unwrap(); + /// let patch: u32 = version_components[2].parse().unwrap(); + /// + /// if major > 3 || (major == 3 && minor >= 38) { + /// /* Valid sqlite version, do nothing */ + /// } else { + /// println!("SQLite version is too old, skipping the test."); + /// return Ok(()); + /// } + /// + /// let result = diesel::select(json_type::(json!({"a": [2, 3.5, true, false, null, "x"]}))) + /// .get_result::(connection)?; + /// + /// assert_eq!("object", result); + /// + /// let result = diesel::select(json_type::(json!({"a": [2, 3.5, true, false, null, "x"]}))) + /// .get_result::(connection)?; + /// + /// assert_eq!("object", result); + /// + /// let result = diesel::select(json_type::, _>(None::)) + /// .get_result::>(connection)?; + /// + /// assert_eq!(None, result); + /// + /// # Ok(()) + /// # } + /// ``` + #[sql_name = "json_type"] + #[cfg(feature = "sqlite")] + fn json_type>(j: J) -> J::Out; + + /// The json_type(X,P) function returns the "type" of the element in X that is selected by path P. + /// If the path P in json_type(X,P) selects an element that does not exist in X, then this function returns NULL. + /// + /// # 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::{sql, json_type_with_path}; + /// # use serde_json::{json, Value}; + /// # use diesel::sql_types::{Text, Json, Jsonb, Nullable}; + /// # let connection = &mut establish_connection(); + /// + /// let version = diesel::select(sql::("sqlite_version();")) + /// .get_result::(connection)?; + /// + /// // Querying SQLite version should not fail. + /// let version_components: Vec<&str> = version.split('.').collect(); + /// let major: u32 = version_components[0].parse().unwrap(); + /// let minor: u32 = version_components[1].parse().unwrap(); + /// let patch: u32 = version_components[2].parse().unwrap(); + /// + /// if major > 3 || (major == 3 && minor >= 38) { + /// /* Valid sqlite version, do nothing */ + /// } else { + /// println!("SQLite version is too old, skipping the test."); + /// return Ok(()); + /// } + /// + /// let json_value = json!({"a": [2, 3.5, true, false, null, "x"]}); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(Some("array".to_string()), result); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a[0]")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(Some("integer".to_string()), result); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a[1]")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(Some("real".to_string()), result); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a[2]")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(Some("true".to_string()), result); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a[6]")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(None, result); + /// + /// let result = diesel::select(json_type_with_path::(json_value.clone(), "$.a")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(Some("array".to_string()), result); + /// + /// let result = diesel::select(json_type_with_path::, _, _>(None::, "$.a")) + /// .get_result::>(connection)?; + /// + /// assert_eq!(None, result); + /// + /// # Ok(()) + /// # } + /// ``` + #[sql_name = "json_type"] + #[cfg(feature = "sqlite")] + fn json_type_with_path( + j: J, + path: Text, + ) -> Nullable; } diff --git a/diesel/src/sqlite/expression/helper_types.rs b/diesel/src/sqlite/expression/helper_types.rs index e13f7fbeebd1..93a451d57f4e 100644 --- a/diesel/src/sqlite/expression/helper_types.rs +++ b/diesel/src/sqlite/expression/helper_types.rs @@ -43,3 +43,14 @@ pub type json_pretty_with_indentation = #[allow(non_camel_case_types)] #[cfg(feature = "sqlite")] pub type json_valid = super::functions::json_valid, E>; + +/// Return type of [`json_type(json)`](super::functions::json_type()) +#[allow(non_camel_case_types)] +#[cfg(feature = "sqlite")] +pub type json_type = super::functions::json_type, E>; + +/// Return type of [`json_type(json, path)`](super::functions::json_type_with_path()) +#[allow(non_camel_case_types)] +#[cfg(feature = "sqlite")] +pub type json_type_with_path = + super::functions::json_type_with_path, J, P>; diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 679252817cd6..22dc3af8da76 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -512,6 +512,8 @@ fn sqlite_functions() -> _ { json_pretty_with_indentation(sqlite_extras::json, " "), json_pretty_with_indentation(sqlite_extras::jsonb, " "), json_valid(sqlite_extras::json), + json_type(sqlite_extras::json), + json_type_with_path(sqlite_extras::json, sqlite_extras::text), ) } From 9bdca6b0b211ca0e242118218f6ec46cbcf8c07b Mon Sep 17 00:00:00 2001 From: Ananth Prathap <22am014@sctce.ac.in> Date: Sun, 2 Mar 2025 05:11:33 +0530 Subject: [PATCH 2/3] fix fmt issues --- diesel/src/sqlite/expression/functions.rs | 4 +++- diesel/src/sqlite/expression/helper_types.rs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/diesel/src/sqlite/expression/functions.rs b/diesel/src/sqlite/expression/functions.rs index b8c60ce53cdf..62eed7710aca 100644 --- a/diesel/src/sqlite/expression/functions.rs +++ b/diesel/src/sqlite/expression/functions.rs @@ -698,7 +698,9 @@ extern "SQL" { /// ``` #[sql_name = "json_type"] #[cfg(feature = "sqlite")] - fn json_type>(j: J) -> J::Out; + fn json_type>( + j: J, + ) -> J::Out; /// The json_type(X,P) function returns the "type" of the element in X that is selected by path P. /// If the path P in json_type(X,P) selects an element that does not exist in X, then this function returns NULL. diff --git a/diesel/src/sqlite/expression/helper_types.rs b/diesel/src/sqlite/expression/helper_types.rs index 93a451d57f4e..3b76625a9e25 100644 --- a/diesel/src/sqlite/expression/helper_types.rs +++ b/diesel/src/sqlite/expression/helper_types.rs @@ -52,5 +52,4 @@ pub type json_type = super::functions::json_type, E>; /// Return type of [`json_type(json, path)`](super::functions::json_type_with_path()) #[allow(non_camel_case_types)] #[cfg(feature = "sqlite")] -pub type json_type_with_path = - super::functions::json_type_with_path, J, P>; +pub type json_type_with_path = super::functions::json_type_with_path, J, P>; From 8134b1de005cd87f2b6966ada612fa09c794bd00 Mon Sep 17 00:00:00 2001 From: Ananth Prathap <22am014@sctce.ac.in> Date: Sun, 2 Mar 2025 05:17:24 +0530 Subject: [PATCH 3/3] fix typo --- examples/postgres/composite_types/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/postgres/composite_types/README.md b/examples/postgres/composite_types/README.md index 8057440c477e..0ade7a48fb44 100644 --- a/examples/postgres/composite_types/README.md +++ b/examples/postgres/composite_types/README.md @@ -72,7 +72,7 @@ let results: Vec = coordinates ### Reducing the output to a single value instead of an array The default output of a query to the database is a table with results. -However, frequenty we may only expect a single answer, especially if a function +However, frequently we may only expect a single answer, especially if a function has defined several **OUT**-*put* parameters instead of returning a table, like the created SQL function ```shortest_distance()```. To avoid the destructering of the vector, when a vector is not needed, we