From c87d0094e34d3f09ed07170337fa2afff452d32f Mon Sep 17 00:00:00 2001 From: insky7 Date: Thu, 15 Aug 2024 18:49:45 -0700 Subject: [PATCH 1/9] Postgres: add array_to_string and array_to_string_null array functions --- diesel/src/pg/expression/functions.rs | 75 ++++++++++++++++++++++++ diesel/src/pg/expression/helper_types.rs | 19 +++++- diesel_derives/tests/auto_type.rs | 30 +++++++++- 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 313958927be8..694850a6b895 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -731,3 +731,78 @@ define_sql_function! { /// ``` fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + /// Converts each array element to its text representation, and concatenates those separated by the delimiter string. + /// If `null_str` is given and is not `NULL`, then `NULL` array entries are represented by that string; + /// otherwise, they are omitted. + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::array_to_string_null; + /// # let conn = &mut establish_connection(); + /// + /// let result_with_null_str = diesel::select(array_to_string_null::, Text>( + /// vec!["a".to_string(), "b".to_string(), "c".to_string()], + /// ", ".to_string(), + /// Some("NULL".to_string()) + /// )).get_result::(conn)?; + /// assert_eq!("a, b, c".to_string(), result_with_null_str); + /// + /// let result_without_null_str = diesel::select(array_to_string_null::, Text>( + /// vec!["a".to_string(), "b".to_string(), None], + /// ", ".to_string(), + /// None + /// )).get_result::(conn)?; + /// assert_eq!("a, b".to_string(), result_without_null_str); + /// + /// # Ok(()) + /// # } + /// ``` + fn array_to_string_null + SingleValue, Elem: SingleValue>( + arr: Arr, + delim: Text, + null_str: Nullable + ) -> Text; +} + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + #[sql_name = "array_to_string"] + /// Converts each array element to its text representation, and concatenates those separated by the delimiter string. + /// This variant omits the `null_str` argument. + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::array_to_string; + /// # let conn = &mut establish_connection(); + /// + /// let result = diesel::select(array_to_string::, Text>( + /// vec!["a".to_string(), "b".to_string(), "c".to_string()], + /// ", ".to_string() + /// )).get_result::(conn)?; + /// assert_eq!("a, b, c".to_string(), result); + /// + /// # Ok(()) + /// # } + /// ``` + fn array_to_string + SingleValue, Elem: SingleValue>( + arr: Arr, + delim: Text + ) -> Text; +} diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index b38e4bc9a68a..567cd0a3ac2e 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -3,7 +3,7 @@ use crate::expression::grouped::Grouped; use crate::expression::Expression; use crate::pg::expression::expression_methods::private::{JsonIndex, JsonRemoveIndex}; use crate::pg::types::sql_types::Array; -use crate::sql_types::{Inet, Integer, VarChar}; +use crate::sql_types::{Inet, Integer, Nullable, Text, VarChar}; /// The return type of [`lhs.ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::ilike) #[cfg(feature = "postgres_backend")] @@ -291,3 +291,20 @@ pub type NotLikeBinary = crate::dsl::NotLike; #[doc(hidden)] #[deprecated(note = "Use `dsl::Concat` instead")] pub type ConcatArray = crate::dsl::Concat; + +/// Return type of [`array_to_string_null(arr, delim, null_str)`](super::functions::array_to_string_null()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type array_to_string_null = super::functions::array_to_string_null< + SqlTypeOf, + SqlTypeOf, + Arr, + Elem, + Nullable, +>; + +/// Return type of [`array_to_string(arr, delim)`](super::functions::array_to_string()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type array_to_string = + super::functions::array_to_string, SqlTypeOf, Arr, Elem>; diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 223c68dce163..d1f634d2cafb 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -385,7 +385,35 @@ fn test_normal_functions() -> _ { case_when(users::id.eq(1_i32), users::id).otherwise(users::id), )) } - +#[cfg(feature = "postgres")] +#[auto_type] +fn postgres_functions() -> _ { + let bound: sql_types::RangeBound = + sql_types::RangeBound::LowerBoundExclusiveUpperBoundExclusive; + ( + lower(pg_extras::range), + upper(pg_extras::range), + isempty(pg_extras::range), + lower_inc(pg_extras::range), + upper_inc(pg_extras::range), + lower_inf(pg_extras::range), + upper_inf(pg_extras::range), + range_merge(pg_extras::range, pg_extras::range), + int4range(users::id.nullable(), users::id.nullable(), bound), + int8range(users::bigint.nullable(), users::bigint.nullable(), bound), + numrange(users::numeric.nullable(), users::numeric.nullable(), bound), + daterange(users::date.nullable(), users::date.nullable(), bound), + tsrange(users::time.nullable(), users::time.nullable(), bound), + tstzrange( + pg_extras::timestamptz.nullable(), + pg_extras::timestamptz.nullable(), + bound, + ), + array_append(pg_extras::array, pg_extras::id), + array_to_string_null(pg_extras::array, ", ".into(), Some("NULL".into())), + array_to_string(pg_extras::array, ", ".to_string()), + ) +} #[auto_type] fn with_lifetime<'a>(name: &'a str) -> _ { users::table.filter(users::name.eq(name)) From 4a51052e0bb78393c2ac08244deb5e7c15159359 Mon Sep 17 00:00:00 2001 From: insky7 Date: Fri, 16 Aug 2024 19:24:21 -0700 Subject: [PATCH 2/9] Add support for array_to_string and array_to_string_null array functions --- diesel/src/pg/expression/functions.rs | 13 ++++++------- diesel/src/pg/expression/helper_types.rs | 16 +++++----------- diesel_derives/tests/auto_type.rs | 2 +- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 5aefd182745a..defb2e1e1d50 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -784,18 +784,18 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_to_string_null + SingleValue, Elem: SingleValue>( + #[sql_name = "array_to_string"] + fn array_to_string_with_null_string( arr: Arr, delim: Text, - null_str: Nullable + null_str: Text ) -> Text; } #[cfg(feature = "postgres_backend")] define_sql_function! { - #[sql_name = "array_to_string"] /// Converts each array element to its text representation, and concatenates those separated by the delimiter string. - /// This variant omits the `null_str` argument. + /// This variant omits the `null_str` argument. See [array_to_string_null] for a variant with that argument /// # Example /// /// ```rust @@ -810,15 +810,14 @@ define_sql_function! { /// # let conn = &mut establish_connection(); /// /// let result = diesel::select(array_to_string::, Text>( - /// vec!["a".to_string(), "b".to_string(), "c".to_string()], - /// ", ".to_string() + /// vec!["a", "b", "c"], "," /// )).get_result::(conn)?; /// assert_eq!("a, b, c".to_string(), result); /// /// # Ok(()) /// # } /// ``` - fn array_to_string + SingleValue, Elem: SingleValue>( + fn array_to_string( arr: Arr, delim: Text ) -> Text; diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index fb9bf969127b..ccd9af9d5159 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -3,7 +3,7 @@ use crate::expression::grouped::Grouped; use crate::expression::Expression; use crate::pg::expression::expression_methods::private::{JsonIndex, JsonRemoveIndex}; use crate::pg::types::sql_types::Array; -use crate::sql_types::{Inet, Integer, Nullable, Text, VarChar}; +use crate::sql_types::{Inet, Integer, Text, VarChar}; /// The return type of [`lhs.ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::ilike) #[cfg(feature = "postgres_backend")] @@ -308,22 +308,16 @@ pub type NotLikeBinary = crate::dsl::NotLike; #[deprecated(note = "Use `dsl::Concat` instead")] pub type ConcatArray = crate::dsl::Concat; -/// Return type of [`array_to_string_null(arr, delim, null_str)`](super::functions::array_to_string_null()) +/// Return type of [`array_to_string_with_null_string(arr, delim, null_str)`](super::functions::array_to_string_with_null_string()) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string_null = super::functions::array_to_string_null< - SqlTypeOf, - SqlTypeOf, - Arr, - Elem, - Nullable, ->; +pub type array_to_string_with_null_string = + super::functions::array_to_string_with_null_string, SqlTypeOf, Arr, Text>; /// Return type of [`array_to_string(arr, delim)`](super::functions::array_to_string()) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string = - super::functions::array_to_string, SqlTypeOf, Arr, Elem>; +pub type array_to_string = super::functions::array_to_string, Arr, Text>; /// Return type of [`lower(range)`](super::functions::lower()) #[allow(non_camel_case_types)] diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 97e3de11d90e..593077636310 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -411,7 +411,7 @@ fn postgres_functions() -> _ { bound, ), array_append(pg_extras::array, pg_extras::id), - array_to_string_null(pg_extras::array, ", ".into(), Some("NULL".into())), + array_to_string_with_null_string(pg_extras::array, ", ".into(), Some("NULL".into())), array_to_string(pg_extras::array, ", ".to_string()), ) } From 6d14d0b564baf72a8c999348fcebf37522102979 Mon Sep 17 00:00:00 2001 From: insky7 Date: Sun, 18 Aug 2024 14:25:50 -0700 Subject: [PATCH 3/9] Add support for array_to_string and array_to_string_with_null --- diesel/src/pg/expression/functions.rs | 92 +++++++++--------------- diesel/src/pg/expression/helper_types.rs | 20 ++++-- diesel_derives/tests/auto_type.rs | 13 +++- 3 files changed, 61 insertions(+), 64 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index defb2e1e1d50..6e63bb1df002 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -711,7 +711,6 @@ define_sql_function! { fn daterange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Daterange; } -#[cfg(feature = "postgres_backend")] define_sql_function! { /// Append an element to the end of an array /// @@ -746,79 +745,58 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } -#[cfg(feature = "postgres_backend")] define_sql_function! { - /// Converts each array element to its text representation, and concatenates those separated by the delimiter string. - /// If `null_str` is given and is not `NULL`, then `NULL` array entries are represented by that string; - /// otherwise, they are omitted. - /// # Example + /// Converts each array element to its text representation, and concatenates those elements + /// separated by the delimiter string. If `null_string` is given and is not `NULL`, then `NULL` + /// array entries are represented by that string; otherwise, they are omitted. /// - /// ```rust - /// # include!("../../doctest_setup.rs"); - /// # - /// # fn main() { - /// # run_test().unwrap(); - /// # } - /// # - /// # fn run_test() -> QueryResult<()> { - /// # use diesel::dsl::array_to_string_null; - /// # let conn = &mut establish_connection(); + /// # Arguments /// - /// let result_with_null_str = diesel::select(array_to_string_null::, Text>( - /// vec!["a".to_string(), "b".to_string(), "c".to_string()], - /// ", ".to_string(), - /// Some("NULL".to_string()) - /// )).get_result::(conn)?; - /// assert_eq!("a, b, c".to_string(), result_with_null_str); + /// * `a` - The array to be converted to a string. + /// * `e` - The delimiter to be used between array elements. + /// * `n` - The string to represent `NULL` values in the array. /// - /// let result_without_null_str = diesel::select(array_to_string_null::, Text>( - /// vec!["a".to_string(), "b".to_string(), None], - /// ", ".to_string(), - /// None - /// )).get_result::(conn)?; - /// assert_eq!("a, b".to_string(), result_without_null_str); + /// # Example /// - /// # Ok(()) - /// # } /// ``` + /// use diesel::dsl::array_to_string_with_null_string; + /// + /// let query = your_table + /// .select(array_to_string_with_null_string(your_array_column, ",", "NULL")); + /// ``` + #[cfg(feature = "postgres_backend")] #[sql_name = "array_to_string"] - fn array_to_string_with_null_string( - arr: Arr, - delim: Text, - null_str: Text + fn array_to_string_with_null_string + SingleValue, T: SingleValue, N: SingleValue>( + a: Arr, + e: T, + n: N ) -> Text; } -#[cfg(feature = "postgres_backend")] define_sql_function! { - /// Converts each array element to its text representation, and concatenates those separated by the delimiter string. - /// This variant omits the `null_str` argument. See [array_to_string_null] for a variant with that argument - /// # Example + /// Converts each array element to its text representation, and concatenates those elements + /// separated by the delimiter string. This variant omits the `null_str` argument. See [`array_to_string_with_null_string`] + /// for a variant with that argument + /// # Arguments /// - /// ```rust - /// # include!("../../doctest_setup.rs"); - /// # - /// # fn main() { - /// # run_test().unwrap(); - /// # } - /// # - /// # fn run_test() -> QueryResult<()> { - /// # use diesel::dsl::array_to_string; - /// # let conn = &mut establish_connection(); + /// * `a` - The array to be converted to a string. + /// * `e` - The delimiter to be used between array elements. /// - /// let result = diesel::select(array_to_string::, Text>( - /// vec!["a", "b", "c"], "," - /// )).get_result::(conn)?; - /// assert_eq!("a, b, c".to_string(), result); + /// # Example /// - /// # Ok(()) - /// # } /// ``` - fn array_to_string( - arr: Arr, - delim: Text + /// use diesel::dsl::array_to_string; + /// + /// let query = your_table + /// .select(array_to_string(your_array_column, ",")); + /// ``` + #[cfg(feature = "postgres_backend")] + fn array_to_string + SingleValue, T: SingleValue>( + a: Arr, + e: T ) -> Text; } diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index ccd9af9d5159..90631c10085f 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -3,7 +3,7 @@ use crate::expression::grouped::Grouped; use crate::expression::Expression; use crate::pg::expression::expression_methods::private::{JsonIndex, JsonRemoveIndex}; use crate::pg::types::sql_types::Array; -use crate::sql_types::{Inet, Integer, Text, VarChar}; +use crate::sql_types::{Inet, Integer, VarChar}; /// The return type of [`lhs.ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::ilike) #[cfg(feature = "postgres_backend")] @@ -311,13 +311,25 @@ pub type ConcatArray = crate::dsl::Concat; /// Return type of [`array_to_string_with_null_string(arr, delim, null_str)`](super::functions::array_to_string_with_null_string()) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string_with_null_string = - super::functions::array_to_string_with_null_string, SqlTypeOf, Arr, Text>; +pub type array_to_string_with_null_string = + super::functions::array_to_string_with_null_string< + SqlTypeOf, // The SQL type of the array + SqlTypeOf, // The SQL type of the delimiter + SqlTypeOf, // The SQL type of the null_string + A, + E, + N, + >; /// Return type of [`array_to_string(arr, delim)`](super::functions::array_to_string()) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string = super::functions::array_to_string, Arr, Text>; +pub type array_to_string = super::functions::array_to_string< + SqlTypeOf, // The SQL type of the array + SqlTypeOf, // The SQL type of the elements within the array + A, + E, +>; /// Return type of [`lower(range)`](super::functions::lower()) #[allow(non_camel_case_types)] diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 593077636310..27f5acefa7d2 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -1,11 +1,12 @@ #![allow(dead_code)] // this is a compile pass test + use diesel::dsl::*; use diesel::helper_types::*; + use diesel::prelude::*; use diesel::sql_types; #[cfg(feature = "postgres")] use std::ops::Bound; - table! { users { id -> Integer, @@ -50,6 +51,8 @@ table! { timestamp -> Timestamp, range -> Range, timestamptz -> Timestamptz, + name -> Text, + array_string -> Array } } @@ -411,8 +414,12 @@ fn postgres_functions() -> _ { bound, ), array_append(pg_extras::array, pg_extras::id), - array_to_string_with_null_string(pg_extras::array, ", ".into(), Some("NULL".into())), - array_to_string(pg_extras::array, ", ".to_string()), + array_to_string(pg_extras::array_string, pg_extras::name), + array_to_string_with_null_string( + pg_extras::array_string, + pg_extras::name, + pg_extras::name.is_null(), + ), ) } From 8d03426c51f20b8de4ef4b0d1b7790f473f4f249 Mon Sep 17 00:00:00 2001 From: insky7 Date: Sun, 18 Aug 2024 14:37:27 -0700 Subject: [PATCH 4/9] Fix formatting --- diesel_derives/tests/auto_type.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 27f5acefa7d2..900b439d00e9 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -1,12 +1,11 @@ #![allow(dead_code)] // this is a compile pass test - use diesel::dsl::*; use diesel::helper_types::*; - use diesel::prelude::*; use diesel::sql_types; #[cfg(feature = "postgres")] use std::ops::Bound; + table! { users { id -> Integer, From 97d4e1f18ea6d5dd3af74fc52f4ec3fa79d587e1 Mon Sep 17 00:00:00 2001 From: insky7 Date: Sun, 18 Aug 2024 14:45:13 -0700 Subject: [PATCH 5/9] Fix doc tests and formatting --- diesel/src/pg/expression/functions.rs | 55 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 6e63bb1df002..323e23fb1c25 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -710,7 +710,7 @@ define_sql_function! { #[cfg(feature = "postgres_backend")] fn daterange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Daterange; } - +#[cfg(feature = "postgres_backend")] define_sql_function! { /// Append an element to the end of an array /// @@ -745,10 +745,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - #[cfg(feature = "postgres_backend")] fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } - +#[cfg(feature = "postgres_backend")] define_sql_function! { /// Converts each array element to its text representation, and concatenates those elements /// separated by the delimiter string. If `null_string` is given and is not `NULL`, then `NULL` @@ -762,13 +761,26 @@ define_sql_function! { /// /// # Example /// - /// ``` - /// use diesel::dsl::array_to_string_with_null_string; + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::array_to_string_with_null_string; + /// # use diesel::sql_types::{Nullable, Text, Array}; + /// # let connection = &mut establish_connection(); + /// + /// let arr = vec![Some("first"), None, Some("third")]; + /// let result = diesel::select(array_to_string_with_null_string::, Text, _>(arr, ",", "NULL")) + /// .get_result::(connection)?; /// - /// let query = your_table - /// .select(array_to_string_with_null_string(your_array_column, ",", "NULL")); + /// assert_eq!(result, "first,NULL,third"); + /// # Ok(()) + /// # } /// ``` - #[cfg(feature = "postgres_backend")] #[sql_name = "array_to_string"] fn array_to_string_with_null_string + SingleValue, T: SingleValue, N: SingleValue>( a: Arr, @@ -777,10 +789,12 @@ define_sql_function! { ) -> Text; } +#[cfg(feature = "postgres_backend")] define_sql_function! { /// Converts each array element to its text representation, and concatenates those elements /// separated by the delimiter string. This variant omits the `null_str` argument. See [`array_to_string_with_null_string`] - /// for a variant with that argument + /// for a variant with that argument. + /// /// # Arguments /// /// * `a` - The array to be converted to a string. @@ -788,13 +802,26 @@ define_sql_function! { /// /// # Example /// - /// ``` - /// use diesel::dsl::array_to_string; + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::array_to_string; + /// # use diesel::sql_types::{Nullable, Text, Array}; + /// # let connection = &mut establish_connection(); /// - /// let query = your_table - /// .select(array_to_string(your_array_column, ",")); + /// let arr = vec![Some("first"), None, Some("third")]; + /// let result = diesel::select(array_to_string::, Text>(arr, ",")) + /// .get_result::(connection)?; + /// + /// assert_eq!(result, "first,third"); + /// # Ok(()) + /// # } /// ``` - #[cfg(feature = "postgres_backend")] fn array_to_string + SingleValue, T: SingleValue>( a: Arr, e: T From 91e6c0b34236f123e614b73b1cf64d1b1fea5da4 Mon Sep 17 00:00:00 2001 From: insky7 Date: Sun, 18 Aug 2024 17:48:14 -0700 Subject: [PATCH 6/9] Fix doctest arguments that were missing --- diesel/src/pg/expression/functions.rs | 28 ++++++++------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index 323e23fb1c25..b6447ffc6fd7 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -753,12 +753,6 @@ define_sql_function! { /// separated by the delimiter string. If `null_string` is given and is not `NULL`, then `NULL` /// array entries are represented by that string; otherwise, they are omitted. /// - /// # Arguments - /// - /// * `a` - The array to be converted to a string. - /// * `e` - The delimiter to be used between array elements. - /// * `n` - The string to represent `NULL` values in the array. - /// /// # Example /// /// ```rust @@ -773,15 +767,14 @@ define_sql_function! { /// # use diesel::sql_types::{Nullable, Text, Array}; /// # let connection = &mut establish_connection(); /// - /// let arr = vec![Some("first"), None, Some("third")]; - /// let result = diesel::select(array_to_string_with_null_string::, Text, _>(arr, ",", "NULL")) + /// let arr = diesel::select(array_to_string_with_null_string::, Text, _, _, _, _>( + /// vec![Some("first"), None, Some("third")], ",", "NULL")) /// .get_result::(connection)?; /// /// assert_eq!(result, "first,NULL,third"); /// # Ok(()) /// # } /// ``` - #[sql_name = "array_to_string"] fn array_to_string_with_null_string + SingleValue, T: SingleValue, N: SingleValue>( a: Arr, e: T, @@ -792,13 +785,8 @@ define_sql_function! { #[cfg(feature = "postgres_backend")] define_sql_function! { /// Converts each array element to its text representation, and concatenates those elements - /// separated by the delimiter string. This variant omits the `null_str` argument. See [`array_to_string_with_null_string`] - /// for a variant with that argument. - /// - /// # Arguments - /// - /// * `a` - The array to be converted to a string. - /// * `e` - The delimiter to be used between array elements. + /// separated by the delimiter string. This variant omits the `null_string` argument. See [`array_to_string_with_null_string`] + /// for a variant with that argument /// /// # Example /// @@ -811,14 +799,14 @@ define_sql_function! { /// # /// # fn run_test() -> QueryResult<()> { /// # use diesel::dsl::array_to_string; - /// # use diesel::sql_types::{Nullable, Text, Array}; + /// # use diesel::sql_types::{Text, Array}; /// # let connection = &mut establish_connection(); /// - /// let arr = vec![Some("first"), None, Some("third")]; - /// let result = diesel::select(array_to_string::, Text>(arr, ",")) + /// let result = diesel::select(array_to_string::, Text, _, _>( + /// vec![Some("first"), Some("second")], ",")) /// .get_result::(connection)?; /// - /// assert_eq!(result, "first,third"); + /// assert_eq!(result, "first,second"); /// # Ok(()) /// # } /// ``` From 2879bf317482e518b7d89a8eba211536a85e5417 Mon Sep 17 00:00:00 2001 From: insky7 Date: Sun, 18 Aug 2024 18:23:14 -0700 Subject: [PATCH 7/9] Ensure Nullable is mapped in SQL context --- diesel/src/pg/expression/functions.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index b6447ffc6fd7..b221b4eb9c69 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -747,7 +747,6 @@ define_sql_function! { /// ``` fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } -#[cfg(feature = "postgres_backend")] define_sql_function! { /// Converts each array element to its text representation, and concatenates those elements /// separated by the delimiter string. If `null_string` is given and is not `NULL`, then `NULL` @@ -767,7 +766,7 @@ define_sql_function! { /// # use diesel::sql_types::{Nullable, Text, Array}; /// # let connection = &mut establish_connection(); /// - /// let arr = diesel::select(array_to_string_with_null_string::, Text, _, _, _, _>( + /// let result = diesel::select(array_to_string_with_null_string::, Nullable, _, _, _, _>( /// vec![Some("first"), None, Some("third")], ",", "NULL")) /// .get_result::(connection)?; /// @@ -775,7 +774,7 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_to_string_with_null_string + SingleValue, T: SingleValue, N: SingleValue>( + fn array_to_string_with_null_string> + SingleValue, T: SingleValue, N: SingleValue>( a: Arr, e: T, n: N @@ -802,7 +801,7 @@ define_sql_function! { /// # use diesel::sql_types::{Text, Array}; /// # let connection = &mut establish_connection(); /// - /// let result = diesel::select(array_to_string::, Text, _, _>( + /// let result = diesel::select(array_to_string::, Nullable, _, _>( /// vec![Some("first"), Some("second")], ",")) /// .get_result::(connection)?; /// @@ -810,7 +809,7 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_to_string + SingleValue, T: SingleValue>( + fn array_to_string> + SingleValue, T: SingleValue>( a: Arr, e: T ) -> Text; From 1876845d7e38c4ab0a57fb6cfac9f4585444adf0 Mon Sep 17 00:00:00 2001 From: insky7 Date: Fri, 23 Aug 2024 11:00:41 -0700 Subject: [PATCH 8/9] fix doc-tests and apply suggested changes --- diesel/src/pg/expression/functions.rs | 65 ++++++++++++++++-------- diesel/src/pg/expression/helper_types.rs | 21 ++++---- diesel_derives/tests/auto_type.rs | 12 ++--- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index b221b4eb9c69..facddffa2af7 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -747,9 +747,10 @@ define_sql_function! { /// ``` fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } +#[cfg(feature = "postgres_backend")] define_sql_function! { - /// Converts each array element to its text representation, and concatenates those elements - /// separated by the delimiter string. If `null_string` is given and is not `NULL`, then `NULL` + /// Converts each array element to its text representation and concatenates those elements + /// separated by the delimiter string. If `null_string` is provided and is not `NULL`, then `NULL` /// array entries are represented by that string; otherwise, they are omitted. /// /// # Example @@ -766,26 +767,38 @@ define_sql_function! { /// # use diesel::sql_types::{Nullable, Text, Array}; /// # let connection = &mut establish_connection(); /// - /// let result = diesel::select(array_to_string_with_null_string::, Nullable, _, _, _, _>( - /// vec![Some("first"), None, Some("third")], ",", "NULL")) - /// .get_result::(connection)?; - /// + /// // Example with `NULL` representation as a string + /// let result: String = diesel::select(array_to_string_with_null_string::>, _, _, _>( + /// vec![Some("first"), None::<&str>, Some("third")], ",", "NULL")) + /// .get_result(connection)?; /// assert_eq!(result, "first,NULL,third"); + /// + /// // Example without any `NULL` values + /// let result: String = diesel::select(array_to_string_with_null_string::>, _, _, _>( + /// vec![Some("first"), Some("second")], ",", "NULL")) + /// .get_result(connection)?; + /// assert_eq!(result, "first,second"); + /// + /// // Example with all `NULL` values + /// let result: String = diesel::select(array_to_string_with_null_string::>, _, _, _>( + /// vec![None::<&str>, None::<&str>], ",", "NULL")) + /// .get_result(connection)?; + /// assert_eq!(result, "NULL,NULL"); + /// /// # Ok(()) /// # } /// ``` - fn array_to_string_with_null_string> + SingleValue, T: SingleValue, N: SingleValue>( - a: Arr, - e: T, - n: N + #[sql_name = "array_to_string"] + fn array_to_string_with_null_string( + array: Arr, del: Text, null: Text ) -> Text; } #[cfg(feature = "postgres_backend")] define_sql_function! { - /// Converts each array element to its text representation, and concatenates those elements - /// separated by the delimiter string. This variant omits the `null_string` argument. See [`array_to_string_with_null_string`] - /// for a variant with that argument + /// Converts each array element to its text representation and concatenates those elements + /// separated by the delimiter string. `NULL` entries are omitted in this variant. + /// See [array_to_string_with_null_string] for a variant with that argument. /// /// # Example /// @@ -798,19 +811,31 @@ define_sql_function! { /// # /// # fn run_test() -> QueryResult<()> { /// # use diesel::dsl::array_to_string; - /// # use diesel::sql_types::{Text, Array}; + /// # use diesel::sql_types::{Text, Array, Nullable}; /// # let connection = &mut establish_connection(); /// - /// let result = diesel::select(array_to_string::, Nullable, _, _>( + /// // Example with non-null values + /// let result: String = diesel::select(array_to_string::>, _, _>( /// vec![Some("first"), Some("second")], ",")) - /// .get_result::(connection)?; - /// + /// .get_result(connection)?; /// assert_eq!(result, "first,second"); + /// + /// // Example with `NULL` values (omitted in the result) + /// let result: String = diesel::select(array_to_string::>, _, _>( + /// vec![Some("first"), None::<&str>, Some("third")], ",")) + /// .get_result(connection)?; + /// assert_eq!(result, "first,third"); + /// + /// // Example with only `NULL` values (empty result) + /// let result: String = diesel::select(array_to_string::>, _, _>( + /// vec![None::<&str>, None::<&str>], ",")) + /// .get_result(connection)?; + /// assert_eq!(result, ""); + /// /// # Ok(()) /// # } /// ``` - fn array_to_string> + SingleValue, T: SingleValue>( - a: Arr, - e: T + fn array_to_string( + array: Arr, del: Text ) -> Text; } diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index 90631c10085f..5c2e9d663aeb 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -308,27 +308,24 @@ pub type NotLikeBinary = crate::dsl::NotLike; #[deprecated(note = "Use `dsl::Concat` instead")] pub type ConcatArray = crate::dsl::Concat; -/// Return type of [`array_to_string_with_null_string(arr, delim, null_str)`](super::functions::array_to_string_with_null_string()) +/// Return type of [`array_to_string_with_null_string(arr, delim, null_str)`](super::functions::array_to_string_with_null_string) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string_with_null_string = +pub type array_to_string_with_null_string = super::functions::array_to_string_with_null_string< SqlTypeOf, // The SQL type of the array - SqlTypeOf, // The SQL type of the delimiter - SqlTypeOf, // The SQL type of the null_string - A, - E, - N, + A, // The array itself + D, // The delimiter + N, // The null string >; -/// Return type of [`array_to_string(arr, delim)`](super::functions::array_to_string()) +/// Return type of [`array_to_string(arr, delim)`](super::functions::array_to_string) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] -pub type array_to_string = super::functions::array_to_string< +pub type array_to_string = super::functions::array_to_string< SqlTypeOf, // The SQL type of the array - SqlTypeOf, // The SQL type of the elements within the array - A, - E, + A, // The array itself + D, // The delimiter >; /// Return type of [`lower(range)`](super::functions::lower()) diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 900b439d00e9..59ec18ca78e8 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -5,7 +5,6 @@ use diesel::prelude::*; use diesel::sql_types; #[cfg(feature = "postgres")] use std::ops::Bound; - table! { users { id -> Integer, @@ -50,8 +49,7 @@ table! { timestamp -> Timestamp, range -> Range, timestamptz -> Timestamptz, - name -> Text, - array_string -> Array + name -> Text } } @@ -413,12 +411,8 @@ fn postgres_functions() -> _ { bound, ), array_append(pg_extras::array, pg_extras::id), - array_to_string(pg_extras::array_string, pg_extras::name), - array_to_string_with_null_string( - pg_extras::array_string, - pg_extras::name, - pg_extras::name.is_null(), - ), + array_to_string(pg_extras::array, pg_extras::name), + array_to_string_with_null_string(pg_extras::array, pg_extras::name, pg_extras::name), ) } From 61fbb93b22063a48128c882036473dec8926cedf Mon Sep 17 00:00:00 2001 From: insky7 Date: Fri, 23 Aug 2024 11:36:45 -0700 Subject: [PATCH 9/9] Fix merge conflicts from helper_types.rs, auto_type.rs, and functions.rs --- diesel/src/pg/expression/functions.rs | 395 +++++++++++++---------- diesel/src/pg/expression/helper_types.rs | 15 + diesel_derives/tests/auto_type.rs | 4 + 3 files changed, 239 insertions(+), 175 deletions(-) diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index facddffa2af7..bebed0e70626 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -1,9 +1,11 @@ //! PostgreSQL specific functions use super::expression_methods::InetOrCidr; -use super::expression_methods::RangeHelper; use crate::expression::functions::define_sql_function; use crate::pg::expression::expression_methods::ArrayOrNullableArray; +use crate::pg::expression::expression_methods::MultirangeOrNullableMultirange; +use crate::pg::expression::expression_methods::MultirangeOrRangeMaybeNullable; +use crate::pg::expression::expression_methods::RangeOrNullableRange; use crate::sql_types::*; define_sql_function! { @@ -75,39 +77,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::lower; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::lower; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Included(7))), - /// versions.eq((Bound::Unbounded, Bound::Included(7))) - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(lower::, _>(1..2)).get_result::>(connection)?; + /// assert_eq!(Some(1), int); /// - /// let cool_posts = posts.select(lower(versions)) - /// .load::>(conn)?; - /// assert_eq!(vec![Some(5), None], cool_posts); + /// let int = diesel::select(lower::, _>(..2)).get_result::>(connection)?; + /// assert_eq!(None, int); + /// + /// let int = diesel::select(lower::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); + /// + /// let int = diesel::select(lower::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(5), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn lower(range: T) -> Nullable<::Inner>; + fn lower(range: R) -> Nullable; } define_sql_function! { @@ -120,39 +115,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::upper; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::upper; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// versions.eq((Bound::Included(5), Bound::Unbounded)) - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(upper::, _>(1..2)).get_result::>(connection)?; + /// assert_eq!(Some(2), int); + /// + /// let int = diesel::select(upper::, _>(1..)).get_result::>(connection)?; + /// assert_eq!(None, int); /// - /// let cool_posts = posts.select(upper(versions)) - /// .load::>(conn)?; - /// assert_eq!(vec![Some(7), None], cool_posts); + /// let int = diesel::select(upper::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); + /// + /// let int = diesel::select(upper::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(7), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn upper(range: T) -> Nullable<::Inner>; + fn upper(range: R) -> Nullable; } define_sql_function! { @@ -163,39 +151,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::isempty; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::isempty; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// versions.eq((Bound::Excluded(7), Bound::Excluded(7))), - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(isempty::, _>(1..5)).get_result::>(connection)?; + /// assert_eq!(Some(false), int); + /// + /// let int = diesel::select(isempty::, _>(1..1)).get_result::>(connection)?; + /// assert_eq!(Some(true), int); + /// + /// let int = diesel::select(isempty::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); /// - /// let cool_posts = posts.select(isempty(versions)) - /// .load::(conn)?; - /// assert_eq!(vec![false, true], cool_posts); + /// let int = diesel::select(isempty::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(false), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn isempty(range: T) -> Bool; + fn isempty(range: R) -> Nullable; } define_sql_function! { @@ -206,39 +187,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::lower_inc; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::lower_inc; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// versions.eq((Bound::Excluded(7), Bound::Excluded(7))), - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(lower_inc::, _>(1..5)).get_result::>(connection)?; + /// assert_eq!(Some(true), int); + /// + /// let int = diesel::select(lower_inc::, _>(..5)).get_result::>(connection)?; + /// assert_eq!(Some(false), int); + /// + /// let int = diesel::select(lower_inc::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); /// - /// let cool_posts = posts.select(lower_inc(versions)) - /// .load::(conn)?; - /// assert_eq!(vec![true, false], cool_posts); + /// let int = diesel::select(lower_inc::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(true), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn lower_inc(range: T) -> Bool; + fn lower_inc(range: R) -> Nullable; } define_sql_function! { @@ -249,38 +223,29 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::upper_inc; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::upper_inc; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(upper_inc::, _>(1..5)).get_result::>(connection)?; + /// assert_eq!(Some(false), int); /// - /// let cool_posts = posts.select(upper_inc(versions)) - /// .load::(conn)?; - /// assert_eq!(vec![false], cool_posts); + /// let int = diesel::select(upper_inc::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); + /// + /// let int = diesel::select(upper_inc::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(false), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn upper_inc(range: T) -> Bool; + fn upper_inc(range: R) -> Nullable; } define_sql_function! { @@ -291,39 +256,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::lower_inf; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::lower_inf; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// versions.eq((Bound::Unbounded, Bound::Excluded(7))), - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(lower_inf::, _>(1..5)).get_result::>(connection)?; + /// assert_eq!(Some(false), int); + /// + /// let int = diesel::select(lower_inf::, _>(..5)).get_result::>(connection)?; + /// assert_eq!(Some(true), int); + /// + /// let int = diesel::select(lower_inf::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); /// - /// let cool_posts = posts.select(lower_inf(versions)) - /// .load::(conn)?; - /// assert_eq!(vec![false, true], cool_posts); + /// let int = diesel::select(lower_inf::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(false), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn lower_inf(range: T) -> Bool; + fn lower_inf(range: R) -> Nullable; } define_sql_function! { @@ -334,39 +292,32 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # versions -> Range, - /// # } - /// # } - /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::upper_inf; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::upper_inf; - /// diesel::insert_into(posts) - /// .values(&[ - /// versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// versions.eq((Bound::Included(5),Bound::Unbounded)), - /// ]).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(upper_inf::, _>(1..5)).get_result::>(connection)?; + /// assert_eq!(Some(false), int); + /// + /// let int = diesel::select(upper_inf::, _>(1..)).get_result::>(connection)?; + /// assert_eq!(Some(true), int); + /// + /// let int = diesel::select(upper_inf::>, _>(None::>)).get_result::>(connection)?; + /// assert_eq!(None, int); /// - /// let cool_posts = posts.select(upper_inf(versions)) - /// .load::(conn)?; - /// assert_eq!(vec![false, true], cool_posts); + /// let int = diesel::select(upper_inf::, _>(vec![5..7])).get_result::>(connection)?; + /// assert_eq!(Some(false), int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn upper_inf(range: T) -> Bool; + fn upper_inf(range: R) -> Nullable; } define_sql_function! { @@ -377,40 +328,63 @@ define_sql_function! { /// ```rust /// # include!("../../doctest_setup.rs"); /// # - /// # table! { - /// # posts { - /// # id -> Integer, - /// # first_versions -> Range, - /// # second_versions -> Range, - /// # } + /// # fn main() { + /// # run_test().unwrap(); /// # } /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::range_merge; + /// # use std::collections::Bound; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(range_merge::, Range<_>, _, _>(5..11, 10..)).get_result::, Bound)>>(connection)?; + /// assert_eq!(Some((Bound::Included(5), Bound::Unbounded)), int); + /// + /// let int = diesel::select(range_merge::, Range<_>, _, _>(1..3, 7..10)).get_result::, Bound)>>(connection)?; + /// assert_eq!(Some((Bound::Included(1), Bound::Excluded(10))), int); + /// + /// let int = diesel::select(range_merge::>, Nullable>, _, _>(None::>, 7..10)).get_result::, Bound)>>(connection)?; + /// assert_eq!(None, int); + /// + /// let int = diesel::select(range_merge::>, Nullable>, _, _>(1..3, None::>)).get_result::, Bound)>>(connection)?; + /// assert_eq!(None, int); + /// # Ok(()) + /// # } + /// ``` + #[cfg(feature = "postgres_backend")] + fn range_merge + SingleValue>(lhs: R1, rhs: R2) -> Nullable>; +} + +define_sql_function! { + /// Returns the smallest range which includes all ranges in the multirange + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # /// # fn main() { /// # run_test().unwrap(); /// # } /// # /// # fn run_test() -> QueryResult<()> { - /// # use self::posts::dsl::*; + /// # use diesel::pg::sql_types::{Range, Multirange}; + /// # use diesel::dsl::multirange_merge; /// # use std::collections::Bound; - /// # let conn = &mut establish_connection(); - /// # diesel::sql_query("DROP TABLE IF EXISTS posts").execute(conn).unwrap(); - /// # diesel::sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, first_versions INT4RANGE NOT NULL, second_versions INT4RANGE NOT NULL)").execute(conn).unwrap(); - /// # - /// use diesel::dsl::range_merge; - /// diesel::insert_into(posts) - /// .values(( - /// first_versions.eq((Bound::Included(5), Bound::Excluded(7))), - /// second_versions.eq((Bound::Included(6),Bound::Unbounded)), - /// )).execute(conn)?; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let int = diesel::select(multirange_merge::, _>(vec![1..3, 7..10])).get_result::<(Bound, Bound)>(connection)?; + /// assert_eq!((Bound::Included(1), Bound::Excluded(10)), int); /// - /// let cool_posts = posts.select(range_merge(first_versions, second_versions)) - /// .load::<(Bound, Bound)>(conn)?; - /// assert_eq!(vec![(Bound::Included(5), Bound::Unbounded)], cool_posts); + /// let int = diesel::select(multirange_merge::>, _>(None::>>)).get_result::, Bound)>>(connection)?; + /// assert_eq!(None, int); /// # Ok(()) /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn range_merge>(lhs: T1, rhs: T2) -> Range; + #[sql_name = "range_merge"] + fn multirange_merge(multirange: R) -> R::Range; } define_sql_function! { @@ -747,6 +721,77 @@ define_sql_function! { /// ``` fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; } + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + /// Replace all occurrences of an element in an array with a given element + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::dsl::array_replace; + /// # use diesel::sql_types::{Nullable, Integer, Array}; + /// # let connection = &mut establish_connection(); + /// let ints = diesel::select(array_replace::, Integer, _, _, _>(vec![1, 2, 5, 4], 5, 3)) + /// .get_result::>(connection)?; + /// assert_eq!(vec![1, 2, 3, 4], ints); + /// + /// let ints = diesel::select(array_replace::, Nullable, _, _, _>(vec![Some(1), Some(2), Some(3)], Some(3), None::)) + /// .get_result::>>(connection)?; + /// assert_eq!(vec![Some(1), Some(2), None], ints); + /// + /// let ints = diesel::select(array_replace::>, Integer, _, _, _>(None::>, 1, 2)) + /// .get_result::>>(connection)?; + /// + /// let ints = diesel::select(array_replace::>, Nullable, _, _, _>(None::>, None::, Some(1))) + /// .get_result::>>>(connection)?; + /// assert_eq!(None, ints); + /// # Ok(()) + /// # } + /// ``` + fn array_replace + SingleValue, T: SingleValue>(a: Arr, e: T, r: T) -> Arr; +} + +#[cfg(feature = "postgres_backend")] +define_sql_function! { + /// Returns a text representation of the array's dimensions + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main(){ + /// # run_test().unwrap(); + /// # } + /// # fn run_test()->QueryResult<()>{ + /// # use diesel::dsl::array_dims; + /// # use diesel::sql_types::{Nullable,Array,Integer}; + /// # let connection = &mut establish_connection(); + /// + /// let dims = diesel::select(array_dims::,_>(vec![1,2])) + /// .get_result::(connection)?; + /// assert!(String::from("[1:2]").eq(&dims)); + /// + /// let dims = diesel::select(array_dims::>,_>(vec![None::,Some(2)])) + /// .get_result::(connection)?; + /// assert!(String::from("[1:2]").eq(&dims)); + /// + /// let dims = diesel::select(array_dims::>,_>(vec![None::])) + /// .get_result::(connection)?; + /// assert!(String::from("[1:1]").eq(&dims)); + /// # Ok(()) + /// # } + /// + fn array_dims + SingleValue>(arr:Arr) -> Text; +} #[cfg(feature = "postgres_backend")] define_sql_function! { /// Converts each array element to its text representation and concatenates those elements diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index 5c2e9d663aeb..e2367ece4184 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -368,7 +368,22 @@ pub type upper_inf = super::functions::upper_inf, R>; #[cfg(feature = "postgres_backend")] pub type range_merge = super::functions::range_merge, SqlTypeOf, R1, R2>; +/// Return type of [`multirange_merge(multirange)`](super::functions::multirange_merge()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type multirange_merge = super::functions::multirange_merge, R>; + /// Return type of [`array_append(array, element)`](super::functions::array_append()) #[allow(non_camel_case_types)] #[cfg(feature = "postgres_backend")] pub type array_append = super::functions::array_append, SqlTypeOf, A, E>; + +/// Return type of [`array_replace(array, element, replace_with)`](super::functions::array_replace()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type array_replace = super::functions::array_replace, SqlTypeOf, A, E, R>; + +/// Return type of [`array_dims(array)`](super::functions::array_append()) +#[allow(non_camel_case_types)] +#[cfg(feature = "postgres_backend")] +pub type array_dims = super::functions::array_dims, A>; diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index 59ec18ca78e8..b65b6e18a925 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -48,6 +48,7 @@ table! { blob -> Binary, timestamp -> Timestamp, range -> Range, + multirange -> Multirange, timestamptz -> Timestamptz, name -> Text } @@ -400,6 +401,7 @@ fn postgres_functions() -> _ { lower_inf(pg_extras::range), upper_inf(pg_extras::range), range_merge(pg_extras::range, pg_extras::range), + multirange_merge(pg_extras::multirange), int4range(users::id.nullable(), users::id.nullable(), bound), int8range(users::bigint.nullable(), users::bigint.nullable(), bound), numrange(users::numeric.nullable(), users::numeric.nullable(), bound), @@ -411,6 +413,8 @@ fn postgres_functions() -> _ { bound, ), array_append(pg_extras::array, pg_extras::id), + array_replace(pg_extras::array, pg_extras::id, pg_extras::id), + array_dims(pg_extras::array), array_to_string(pg_extras::array, pg_extras::name), array_to_string_with_null_string(pg_extras::array, pg_extras::name, pg_extras::name), )