From 8335dc2e62b8b63ad635d302926adef8151f71c7 Mon Sep 17 00:00:00 2001 From: meeshal Date: Sat, 19 Oct 2024 09:09:29 +0200 Subject: [PATCH 1/5] add is_json and is_not_json expression methods --- .../src/pg/expression/expression_methods.rs | 76 +++++++++++++++++++ diesel/src/pg/expression/helper_types.rs | 8 ++ diesel/src/pg/expression/operators.rs | 2 + 3 files changed, 86 insertions(+) diff --git a/diesel/src/pg/expression/expression_methods.rs b/diesel/src/pg/expression/expression_methods.rs index a2903f5cb17f..657d296c9fbc 100644 --- a/diesel/src/pg/expression/expression_methods.rs +++ b/diesel/src/pg/expression/expression_methods.rs @@ -157,6 +157,82 @@ pub trait PgExpressionMethods: Expression + Sized { { Grouped(IsContainedBy::new(self, other.as_expression())) } + + /// Creates a PostgreSQL `IS JSON` expression. + /// Requires PostgreSQL>=16 + /// + /// This operator returns true whether an object is a valid JSON + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use std::collections::Bound; + /// # use diesel::sql_types::Text; + /// # + /// # let conn = &mut establish_connection(); + /// # + /// + /// let res = diesel::select(("1".into_sql::().is_json())).get_result::(conn)?; + /// assert_eq!(res, true); + /// let res = diesel::select(("[1,2,3]".into_sql::().is_json())).get_result::(conn)?; + /// assert_eq!(res, true); + /// let res = diesel::select(("{\"products\": [1,2,3]}".into_sql::().is_json())).get_result::(conn)?; + /// assert_eq!(res, true); + /// let res = diesel::select(("(1,2,3)".into_sql::().is_json())).get_result::(conn)?; + /// assert_eq!(res, false); + /// # + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::wrong_self_convention)] // This is named after the sql operator + fn is_json(self) -> dsl::IsJson { + IsJson::new(self) + } + + /// Creates a PostgreSQL `IS NOT JSON` expression. + /// Requires PostgreSQL>=16 + /// + /// This operator returns true whether an object is not a valid JSON + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use std::collections::Bound; + /// # use diesel::sql_types::Text; + /// # + /// # let conn = &mut establish_connection(); + /// # + /// + /// let res = diesel::select(("1".into_sql::().is_not_json())).get_result::(conn)?; + /// assert_eq!(res, false); + /// let res = diesel::select(("[1,2,3]".into_sql::().is_not_json())).get_result::(conn)?; + /// assert_eq!(res, false); + /// let res = diesel::select(("{\"products\": [1,2,3]}".into_sql::().is_not_json())).get_result::(conn)?; + /// assert_eq!(res, false); + /// let res = diesel::select(("(1,2,3)".into_sql::().is_not_json())).get_result::(conn)?; + /// assert_eq!(res, true); + /// # + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::wrong_self_convention)] // This is named after the sql operator + fn is_not_json(self) -> dsl::IsNotJson { + IsNotJson::new(self) + } } impl PgExpressionMethods for T {} diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs index 856c135e4094..51b4908685ad 100644 --- a/diesel/src/pg/expression/helper_types.rs +++ b/diesel/src/pg/expression/helper_types.rs @@ -130,6 +130,14 @@ pub type IntersectionRange = Intersection; #[cfg(feature = "postgres_backend")] pub type NullsFirst = super::operators::NullsFirst; +/// The return type of [`expr.is_json()`](super::expression_methods::PgExpressionMethods::is_json) +#[cfg(feature = "postgres_backend")] +pub type IsJson = super::operators::IsJson; + +/// The return type of [`expr.is_not_json()`](super::expression_methods::PgExpressionMethods::is_not_json) +#[cfg(feature = "postgres_backend")] +pub type IsNotJson = super::operators::IsNotJson; + /// The return type of [`expr.nulls_last()`](super::expression_methods::PgSortExpressionMethods::nulls_last) #[cfg(feature = "postgres_backend")] pub type NullsLast = super::operators::NullsLast; diff --git a/diesel/src/pg/expression/operators.rs b/diesel/src/pg/expression/operators.rs index 4bc660ecb09c..261b8fc945fa 100644 --- a/diesel/src/pg/expression/operators.rs +++ b/diesel/src/pg/expression/operators.rs @@ -23,6 +23,8 @@ infix_operator!(SimilarTo, " SIMILAR TO ", backend: Pg); infix_operator!(NotSimilarTo, " NOT SIMILAR TO ", backend: Pg); postfix_operator!(NullsFirst, " NULLS FIRST", NotSelectable, backend: Pg); postfix_operator!(NullsLast, " NULLS LAST", NotSelectable, backend: Pg); +postfix_operator!(IsJson, " IS JSON", backend: Pg); +postfix_operator!(IsNotJson, " IS NOT JSON", backend: Pg); infix_operator!(ContainsNet, " >> ", backend: Pg); infix_operator!(ContainsNetLoose, " >>= ", backend: Pg); infix_operator!(IsContainedByNet, " << ", backend: Pg); From 8094feaac4ae236673c16c2b483c35ddd214dbe4 Mon Sep 17 00:00:00 2001 From: meeshal Date: Sat, 19 Oct 2024 16:51:45 +0200 Subject: [PATCH 2/5] overwrite diesel_compile_tests --- .../tests/fail/numeric_ops_require_numeric_column.stderr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr index cb1cb547e6b2..53acf55fb277 100644 --- a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr +++ b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr @@ -13,3 +13,6 @@ note: an implementation of `std::ops::Add` might be missing for `columns::name` | ^^^^ must implement `std::ops::Add` note: the trait `std::ops::Add` must be implemented --> $RUST/core/src/ops/arith.rs + | + | pub trait Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 66475d2e50e33ee641a3359225d9d7253d36a398 Mon Sep 17 00:00:00 2001 From: meeshal Date: Sat, 19 Oct 2024 17:12:41 +0200 Subject: [PATCH 3/5] add no_run specifier to is_json and is_not_json doc tests as psql>=16.0 is required --- diesel/src/pg/expression/expression_methods.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diesel/src/pg/expression/expression_methods.rs b/diesel/src/pg/expression/expression_methods.rs index 657d296c9fbc..614ae3f97f11 100644 --- a/diesel/src/pg/expression/expression_methods.rs +++ b/diesel/src/pg/expression/expression_methods.rs @@ -165,7 +165,7 @@ pub trait PgExpressionMethods: Expression + Sized { /// /// # Example /// - /// ```rust + /// ```rust,no_run /// # include!("../../doctest_setup.rs"); /// # /// # fn main() { @@ -203,7 +203,7 @@ pub trait PgExpressionMethods: Expression + Sized { /// /// # Example /// - /// ```rust + /// ```rust,no_run /// # include!("../../doctest_setup.rs"); /// # /// # fn main() { From 3a9d9d11d6b01463aa3239bed7e5c0f01dc68f7b Mon Sep 17 00:00:00 2001 From: meeshal Date: Sat, 19 Oct 2024 17:15:30 +0200 Subject: [PATCH 4/5] overwrite diesel_compile_tests --- .../tests/fail/numeric_ops_require_numeric_column.stderr | 3 --- 1 file changed, 3 deletions(-) diff --git a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr index 53acf55fb277..cb1cb547e6b2 100644 --- a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr +++ b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr @@ -13,6 +13,3 @@ note: an implementation of `std::ops::Add` might be missing for `columns::name` | ^^^^ must implement `std::ops::Add` note: the trait `std::ops::Add` must be implemented --> $RUST/core/src/ops/arith.rs - | - | pub trait Add { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ From c41b5b7d2226fe464e7eca76d3e4d37edc2594d4 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 25 Oct 2024 11:17:38 +0200 Subject: [PATCH 5/5] Also add the new operator to the `#[auto_type]` test --- diesel_derives/tests/auto_type.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs index f9cefcf69570..8847b1d040f2 100644 --- a/diesel_derives/tests/auto_type.rs +++ b/diesel_derives/tests/auto_type.rs @@ -281,6 +281,8 @@ fn test_pg_jsonb_expression_methods() -> _ { .and(pg_extras::jsonb.remove(1_i32).eq(pg_extras::jsonb)) .and(pg_extras::jsonb.remove_by_path(v).eq(pg_extras::jsonb)) .and(pg_extras::jsonb.is_contained_by(pg_extras::jsonb)) + .and(pg_extras::id.is_json()) + .and(pg_extras::id.is_not_json()) } #[cfg(feature = "postgres")]