From 39cd8feefea2b9a21c7f2148fdf730360a1cf0d6 Mon Sep 17 00:00:00 2001 From: "ali.tariq" Date: Thu, 12 Sep 2024 11:10:50 +0500 Subject: [PATCH 1/3] implement jsonb_object (both variants) --- diesel/src/pg/expression/functions.rs | 93 ++++++++++++++++++++++++ diesel/src/pg/expression/helper_types.rs | 11 +++ diesel_derives/tests/auto_type.rs | 2 + 3 files changed, 106 insertions(+) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index cc793ae3092b..640ee2dc808e 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -2078,3 +2078,96 @@ define_sql_function! { fn jsonb_array_length>(jsonb: E) -> E::Out; } + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + /// Builds a JSON object out of a text array. The array must have an even number of members, + /// in which case they are taken as alternating key/value pairs. This function also has a form that + /// that takes keys and values as separate text array arguments. + /// See [jsonb_object_with_keys_and_values] + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::jsonb_object; + /// # use diesel::sql_types::{Array, Json, Nullable, Text}; + /// # use serde_json::Value; + /// # let connection = &mut establish_connection(); + /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world"])) + /// .get_result::(connection)?; + /// let expected:Value = serde_json::json!({"hello":"world"}); + /// assert_eq!(expected,jsonb); + /// + /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world","John","Doe"])) + /// .get_result::(connection)?; + /// let expected:Value = serde_json::json!({"hello": "world","John": "Doe"}); + /// assert_eq!(expected,jsonb); + /// + /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world","John"])) + /// .get_result::(connection); + /// assert!(jsonb.is_err()); + /// + /// let empty:Vec = Vec::new(); + /// let jsonb = diesel::select(jsonb_object::>,_>(empty)) + /// .get_result::(connection); + /// assert!(jsonb.is_err()); + /// + /// # Ok(()) + /// # } + /// ``` + fn jsonb_object>( + text_array: Arr, + ) -> Arr::Out; +} + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + /// This form of jsonb_object takes keys and values pairwise from two separate arrays. + /// In all other respects it is identical to the one-argument form. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::jsonb_object_with_keys_and_values; + /// # use diesel::sql_types::{Array, Nullable, Text}; + /// # use serde_json::Value; + /// # let connection = &mut establish_connection(); + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, _, _>( + /// vec!["hello","John"],vec!["world","Doe"])) + /// .get_result::(connection)?; + /// let expected:Value = serde_json::json!({"hello":"world","John":"Doe"}); + /// assert_eq!(expected,jsonb); + /// + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::>, _, _>( + /// Some(vec!["hello","John"]),None::>)) + /// .get_result::>(connection)?; + /// assert_eq!(None::,jsonb); + /// + /// let empty: Vec = Vec::new(); + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, _, _>( + /// vec!["hello","John"],empty)) + /// .get_result::(connection); + /// assert!(jsonb.is_err()); + /// + /// # Ok(()) + /// # } + /// ``` + #[sql_name = "jsonb_object"] + fn jsonb_object_with_keys_and_values>( + keys: Arr, values: Arr + ) -> Arr::Out; +} diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index c5394a1ecd5d..fd9b197ee6c1 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -527,3 +527,14 @@ pub type json_array_length = super::functions::json_array_length #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] pub type jsonb_array_length = super::functions::jsonb_array_length, E>; + +/// Return type of [`jsonb_object(text_array)`](super::functions::jsonb_object()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type jsonb_object = super::functions::jsonb_object, A>; + +/// Return type of [`jsonb_object_with_keys_and_values(text_array, text_array)`](super::functions::jsonb_object_with_keys_and_values()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type jsonb_object_with_keys_and_values = + super::functions::jsonb_object_with_keys_and_values, K, V>; diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index deaedaec3dc4..015f533fe947 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -446,6 +446,8 @@ fn postgres_functions() -> _ { jsonb_strip_nulls(pg_extras::jsonb), json_array_length(pg_extras::json), jsonb_array_length(pg_extras::jsonb), + jsonb_object(pg_extras::text_array), + jsonb_object_with_keys_and_values(pg_extras::text_array, pg_extras::text_array), ) } From 6aa3f2387dea0cd31f824be4a16dc37e56d924f3 Mon Sep 17 00:00:00 2001 From: "ali.tariq" Date: Mon, 16 Sep 2024 18:26:26 +0500 Subject: [PATCH 2/3] added null doctest and fixed type signature --- diesel/src/pg/expression/functions.rs | 36 +++++++++++++++--------- diesel/src/pg/expression/helper_types.rs | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 640ee2dc808e..24e1f538ef61 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -2103,22 +2103,28 @@ define_sql_function! { /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world"])) /// .get_result::(connection)?; /// let expected:Value = serde_json::json!({"hello":"world"}); - /// assert_eq!(expected,jsonb); + /// assert_eq!(expected, jsonb); /// - /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world","John","Doe"])) + /// let jsonb = diesel::select(jsonb_object::, _>(vec!["hello","world","John","Doe"])) /// .get_result::(connection)?; /// let expected:Value = serde_json::json!({"hello": "world","John": "Doe"}); - /// assert_eq!(expected,jsonb); + /// assert_eq!(expected, jsonb); /// - /// let jsonb = diesel::select(jsonb_object::,_>(vec!["hello","world","John"])) - /// .get_result::(connection); - /// assert!(jsonb.is_err()); + /// let jsonb = diesel::select(jsonb_object::>, _>(None::>)) + /// .get_result::>(connection)?; + /// assert!(jsonb.is_none()); /// /// let empty:Vec = Vec::new(); /// let jsonb = diesel::select(jsonb_object::>,_>(empty)) + /// .get_result::(connection)?; + /// let expected = serde_json::json!({}); + /// assert_eq!(expected, jsonb); + /// + /// let jsonb = diesel::select(jsonb_object::, _>(vec!["hello","world","John"])) /// .get_result::(connection); /// assert!(jsonb.is_err()); /// + /// /// # Ok(()) /// # } /// ``` @@ -2146,19 +2152,19 @@ define_sql_function! { /// # use diesel::sql_types::{Array, Nullable, Text}; /// # use serde_json::Value; /// # let connection = &mut establish_connection(); - /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, _, _>( + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, Array, _, _>( /// vec!["hello","John"],vec!["world","Doe"])) /// .get_result::(connection)?; /// let expected:Value = serde_json::json!({"hello":"world","John":"Doe"}); - /// assert_eq!(expected,jsonb); + /// assert_eq!(expected, jsonb); /// - /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::>, _, _>( + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::>, Nullable>, _, _>( /// Some(vec!["hello","John"]),None::>)) /// .get_result::>(connection)?; /// assert_eq!(None::,jsonb); /// /// let empty: Vec = Vec::new(); - /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, _, _>( + /// let jsonb = diesel::select(jsonb_object_with_keys_and_values::, Array, _, _>( /// vec!["hello","John"],empty)) /// .get_result::(connection); /// assert!(jsonb.is_err()); @@ -2167,7 +2173,11 @@ define_sql_function! { /// # } /// ``` #[sql_name = "jsonb_object"] - fn jsonb_object_with_keys_and_values>( - keys: Arr, values: Arr - ) -> Arr::Out; + fn jsonb_object_with_keys_and_values< + Arr1: TextArrayOrNullableTextArray + SingleValue, + Arr2: TextArrayOrNullableTextArray + CombinedNullableValue + >( + keys: Arr1, + values: Arr2 + ) -> Arr2::Out; } diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index fd9b197ee6c1..efc1d8e68975 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -537,4 +537,4 @@ pub type jsonb_object = super::functions::jsonb_object, A>; #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] pub type jsonb_object_with_keys_and_values = - super::functions::jsonb_object_with_keys_and_values, K, V>; + super::functions::jsonb_object_with_keys_and_values, SqlTypeOf, K, V>; From 307e9db9df36e73e108e9cebfaf738adf2960ba1 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 18 Sep 2024 11:43:43 +0200 Subject: [PATCH 3/3] Correctly put some tests behind `#[cfg(feature = "serde_json")]` --- diesel/src/pg/expression/functions.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 24e1f538ef61..2feb1ce4d0f0 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -1669,9 +1669,11 @@ define_sql_function! { /// # 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_object_with_keys_and_values; /// # use diesel::sql_types::{Array, Json, Nullable, Text}; @@ -1913,9 +1915,11 @@ define_sql_function! { /// # 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_strip_nulls; /// # use diesel::sql_types::{Json, Nullable}; @@ -1957,9 +1961,11 @@ define_sql_function! { /// # 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_strip_nulls; /// # use diesel::sql_types::{Jsonb, Nullable}; @@ -2092,9 +2098,11 @@ define_sql_function! { /// # 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_object; /// # use diesel::sql_types::{Array, Json, Nullable, Text}; @@ -2144,9 +2152,11 @@ define_sql_function! { /// # 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_object_with_keys_and_values; /// # use diesel::sql_types::{Array, Nullable, Text};