Repository: diesel-rs/diesel (language: rust)

Issue:
Add support for currently unsupported JSON/JSONB operators
Diesel currently supports the postgres json and jsonb types. We do not provide built-in support for [various operators](https://www.postgresql.org/docs/current/functions-json.html) available for these types. This is a tracking issue for adding support for these operators. 

The general strategy for adding support for new operators is as following:

1. Define the operator via [`infix_operator!()`](https://docs.diesel.rs/master/diesel/macro.infix_operator.html). The contains operator  would be defined as following: `infix_operator!(KeyExists, " ? ", backend: Pg);` These operators can be defined [here](https://github.com/diesel-rs/diesel/blob/dd5f41dc93c574077646185393cd064c7103000b/diesel/src/pg/expression/operators.rs). If there is already an existing definition, this step could be skipped.
2. Define a new helper type [here](https://github.com/diesel-rs/diesel/blob/master/diesel/src/pg/expression/helper_types.rs#L30). Helper types are a tool for diesel users to simplify type definitions. Again if there is already a matching type definition this could be skipped. 
3. Add a new `ExpressionMethods` extension trait for the corresponding types. This only needs to happen once for `jsonb`   and once for `jsonb` + `json`.  See the [`PgRangeExpressionMethods` trait](https://github.com/diesel-rs/diesel/blob/dd5f41dc93c574077646185393cd064c7103000b/diesel/src/pg/expression/expression_methods.rs#L621-L690) here as example.
4. Add a new method to the corresponding `ExpressionMethods` trait. See [here](https://github.com/diesel-rs/diesel/blob/dd5f41dc93c574077646185393cd064c7103000b/diesel/src/pg/expression/expression_methods.rs#L143-L200) for an example for a different operator/type. This definition should:
    * Use the previously defined helper type
    * Contain a documentation with an example
5. Check if compile test output is up to date by running `TRYBUILD=overwrite cargo test` inside of `diesel_compile_tests`. (This is required if you've added a new operator based on `infix_operator!`
5. Submit a PR with the change

Operator list:

- [ ] `json/jsonb -> integer` (indexing by index, returns `json/jsonb`) **
- [ ] `json/jsonb -> text` (indexing by key name, returns `json/jsonb`) **
- [ ] `json/jsonb ->> integer` (indexing by index, returns field as `text`) **
- [ ] `json/jsonb ->> text` (indexing by key name, returns field as `text`) **
- [ ] `json/jsonb #> text[]` (indexing by path, returns field as `json/jsonb`) *
- [ ] `json/jsonb #>> text[]` (indexing by path, returns field as `text`) *
- [x] `jsonb @> jsonb` (checks if the second value is contained by the first value, returns a `boolean`) *
- [x] `jsonb <@ jsonb` (checks if the first value is contained by the second value, returns a `boolean`) *
- [x] `jsonb ? text` (checks if a string exists as top level key in the given json, returns a `boolean`) *
- [x] `jsonb ?| text[]` (checks if any of the strings exists as top level key in the given json, returns a `boolean`) *
- [x] `jsonb ?& text[]` (checks if all of the strings exist as top level key in the given json, returns a `boolean`) *
- [x] `jsonb || jsonb` (concats two json values, returns `jsonb`) *
- [ ] `jsonb - text` (remove the key given as string from the json, returns `jsonb`) **
- [ ] `jsonb - text[]` (removes all keys given as string from the json, returns `jsonb`) **
- [ ] `jsonb - integer` (removes the field with the given index from the json, returns `jsonb`) **
- [ ] `jsonb #- text[]` (removes the given path from the json, returns `jsonb`) *
- [ ] `jsonb @? jsonpath` (Does JSON path return any item for the specified JSON value?, returns a `boolean`) ***
- [ ] `jsonb @@ jsonpath (Returns the result of a JSON path predicate check for the specified JSON value. Only the first item of the result is taken into account. If the result is not Boolean, then NULL is returned., returns a `boolean`) ***

Items marked with one `*` can follow the instructions directly, items marked with `**` require additional work to unify the different input types behind a common operator/function call (See [here](https://github.com/diesel-rs/diesel/blob/dd5f41dc93c574077646185393cd064c7103000b/diesel/src/pg/expression/expression_methods.rs#L551-L561) for a general example on how to abstract over expressions of different types), items marked with `***` likely require additional design work.

Please add a comment to this issue if you plan to work on a specific operator.

If there is anything unclear about how to add a support for a specific operator just ask and we will try to answer your questions.
Implement JSONB remove key operators
Implemented the following operators:
```
jsonb - text
jsonb - text[]
jsonb - integer
```


Discussion / hints:
Believe it or not, I was considering suggesting this some time last week!
I will gladly take this task up for the `one asterisk` operators and maybe this work will evolve into discussion on other operators.

Edit:
on the second thought, this probably shouldn't all be done by one person.
First operator I will work on:
`jsonb || jsonb`

@weiznich what version do we add this in, current or master?
@czotomo Any new feature should target the master branch.
Jsonb concat PR: https://github.com/diesel-rs/diesel/pull/3035
Alright, let's continue with the trivial ones.
I'm going to implement
 `jsonb ? text` 

Edit:
PR https://github.com/diesel-rs/diesel/pull/3036
Alright, I'll implement the array and object indexing operators by ints and strings, I don't see a reason to split this work.
 json/jsonb -> integer => json/jsonb
 json/jsonb -> text => json/jsonb
 json/jsonb ->> integer  => text
 json/jsonb ->> text => text
Hey, I'd like to get my hands dirty with these two operators if that's ok..
`jsonb ?| text[]` 
`jsonb ?& text[]`
@czotomo You can likely combine these two operations
> json/jsonb -> integer => json/jsonb
> json/jsonb -> text => json/jsonb

in one function by using a similar trick as for implementing the `PgJsonbExxpressionMethods` trait for jsonb + nullable jsonb. To do that you can use a generic type for the sqltype passed into `AsExpression` and restrict that type to `Integer` or `Text` by using am auxiliary trait.

(Also these operators need there own ExpressionMethods trait as these need to be implemented for the Json type as well. Again thats possible by using a corresponding auxiliary trait implemented for jsonb and json + their nullable variants.)

@mrbuzz :+1: go for it
Hi, I'd like to try adding support for these two operators.
`jsonb @> jsonb`
`jsonb <@ jsonb`

@czotomo Would it be possible to team up on this? Quite new to Rust, but have plenty of development experience. I'd be happy with just being a fly on the wall, and just see how the problem is solved.
@gamesbrainiac sure, I did not do too much work on these four yet. Please reach out to me on gitter (same handle as on Github) so we can sync.
I'd like to take these three operators as my next task:
 `jsonb - text`
 `jsonb - text[]`
 `jsonb - integer`
I might need some guidance as I'm relatively new to Rust, I'll start by looking at the example code
Me and @czotomo have made some progress, but we're a little stuck. If @mrbuzz wants to join in, we plan of having another crack at it tomorrow.
@gamesbrainiac @czotomo Is there any information that I could provide to help you? As additional side note: Opening a WIP PR could also be a good idea, as this makes it easier for others to comment on specific code issues, as they have access to the whole change.
@mrbuzz I'm happy to answer any question that comes up. Just ask .
I'll try these operators next.
`json/jsonb #> text[]`
`json/jsonb #>> text[]`

Edit: they aren't marked as `**` like the others above them which also operate on `json/jsonb`. Won't they also require a marker trait for `json/jsonb` ?
> Me and @czotomo have made some progress, but we're a little stuck. If @mrbuzz wants to join in, we plan of having another crack at it tomorrow.

That'd be cool! Thanks for the invite. At what time are you meeting? I am a little short on time today but I'll see what  I can do 
@zaneduffield Yes those would need marker traits for json/jsonb. So basically this thing here: https://github.com/diesel-rs/diesel/blob/b479e8352f6712bbc2f6bdcd734c8e32a934e6b9/diesel/src/pg/expression/expression_methods.rs#L1554-L1564

but so that the helper trait (`JsonbOrNullableJsonb`) is implemented for `Json` and `Nullable<Json>` as well.

This is the progress we've made so far. https://github.com/gamesbrainiac/diesel/commit/3e857bbe395206aad5ed5d1eff651098d19bebb4

We tried the tests, but it keeps failing. It _should_ work, but it's hard to figure out what's wrong.

@mrbuzz Do you have gitter? We usually communicate using that.

@weiznich is there a diesel development / maintenance Gitter room where people who know ins and outs hang out, or are the conversations strictly performed in issues' and PRs' comments?
@gamesbrainiac
The following change fixes the test:
```diff
@@ -1363,7 +1363,7 @@ pub trait PgAnyJsonExpressionMethods: Expression + Sized {
     ///     .values((name.eq("Claus"), address.eq(&santas_address)))
     ///     .execute(conn)?;
     ///
-    /// let santas_postcode = contacts.select(address.retrieve_as_object("postcode")).get_result::<serde_json::Value>(conn)?;
+    /// let santas_postcode = contacts.select(address.retrieve_as_object::<diesel::sql_types::Text, _>("postcode")).get_result::<serde_json::Value>(conn)?;
     /// assert_eq!(santas_postcode, serde_json::json!("99705"));
     ///
     ///
@@ -1388,7 +1388,8 @@ pub trait PgAnyJsonExpressionMethods: Expression + Sized {
     ///
     /// let roberts_second_address_in_db = contacts
     ///                             .filter(name.eq("Robert Downey Jr."))
-    ///                             .select(address.retrieve_as_object(1))
+    ///                             .select(address.retrieve_as_object::<
+    ///                             diesel::sql_types::Integer, _>(1))
     ///                             .get_result::<serde_json::Value>(conn)?;
     ///
     /// let roberts_second_address = serde_json::json!({
@@ -1405,9 +1406,9 @@ pub trait PgAnyJsonExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     /// ```
-    fn retrieve_as_object<T, ST>(self, other: T) -> dsl::RetrieveAsObjectJsonb<Self, T, ST>
+    fn retrieve_as_object<ST, T>(self, other: T) -> dsl::RetrieveAsObjectJsonb<Self, T, ST>
     where
-        ST: SqlType + TypedExpressionType,
+        ST: SqlType + TypedExpressionType + TextOrInteger,
         T: AsExpression<ST>,
     {
         Grouped(RetrieveAsObjectJsonb::new(self, other.as_expression()))
@@ -1432,6 +1433,5 @@ impl<T> PgAnyJsonExpressionMethods for T
 where
     Self::SqlType: JsonOrNullableJsonOrJsonbOrNullableJsonb,
     T: Expression,
-    T::SqlType: TextOrInteger,
 {
 }
```

To explain that a bit:

The change in line 1432 (removing `T::SqlType: TextOrInteger`) fixes the problem that the trait is not implemented for any jsonb column. That happens because that condition reads like: If the column is (a json or jsonb or nullable json or nullable jsonb) and (text or integer). That something that cannot be true at any point of time, as a column has just one sql type. What we cant here is to assert that the left side of the expression is `JsonOrNullableJsonOrJsonbOrNullableJsonb` while the **right** side is `TextOrInteger`. So we remove the trait bound here and add it in line 1405, where other represents the right side of the expression.

The other changes work around the fact that rustc cannot infer the correct sql type for `&str` and `integer`, so that they must be specified directly. That's a bit unfortunate, as this makes the current interface hard to use. I would like to hear opinions here so that we hopefully can find a better solution. 

@czotomo It's fine to ask these question in the normal gitter room, but if there is need for that we can just create a new room for that.
Perhaps we have two methods? Or perhaps we always get text, and cast it later to Integer? You're right, this looks very ugly.


Fix patch:
diff --git a/diesel/src/expression/operators.rs b/diesel/src/expression/operators.rs
index 0b6475c0a927..96ff8406c30d 100644
--- a/diesel/src/expression/operators.rs
+++ b/diesel/src/expression/operators.rs
@@ -273,7 +273,15 @@ macro_rules! __diesel_infix_operator {
             backend_ty = DB,
         );
     };
-
+    ($name:ident, $operator:expr, __diesel_internal_SameResultAsInput, backend: $backend:ty) => {
+        $crate::__diesel_infix_operator!(
+            name = $name,
+            operator = $operator,
+            return_ty = (<T as $crate::expression::Expression>::SqlType),
+            backend_ty_params = (),
+            backend_ty = $backend,
+        );
+    };
     ($name:ident, $operator:expr, ConstantNullability $($return_ty:tt)::*, backend: $backend:ty) => {
         $crate::__diesel_infix_operator!(
             name = $name,
diff --git a/diesel/src/pg/expression/expression_methods.rs b/diesel/src/pg/expression/expression_methods.rs
index 674ecfa99401..939e1403e7ca 100644
--- a/diesel/src/pg/expression/expression_methods.rs
+++ b/diesel/src/pg/expression/expression_methods.rs
@@ -1,15 +1,16 @@
 //! PostgreSQL specific expression methods
 
 pub(in crate::pg) use self::private::{
-    ArrayOrNullableArray, InetOrCidr, JsonbOrNullableJsonb, RangeHelper, RangeOrNullableRange,
-    TextOrNullableText,
+    ArrayOrNullableArray, InetOrCidr, JsonIndex, JsonOrNullableJsonOrJsonbOrNullableJsonb,
+    JsonRemoveIndex, JsonbOrNullableJsonb, RangeHelper, RangeOrNullableRange, TextOrNullableText,
 };
+use super::date_and_time::{AtTimeZone, DateTimeLike};
 use super::operators::*;
 use crate::dsl;
 use crate::expression::grouped::Grouped;
 use crate::expression::operators::{Asc, Desc};
 use crate::expression::{AsExpression, Expression, IntoSql, TypedExpressionType};
-use crate::sql_types::{Array, Inet, Integer, Jsonb, SqlType, Text};
+use crate::sql_types::{Array, Inet, Integer, Jsonb, SqlType, Text, VarChar};
 use crate::EscapeExpressionMethods;
 
 /// PostgreSQL specific methods which are present on all expressions.
@@ -74,9 +75,6 @@ pub trait PgExpressionMethods: Expression + Sized {
 
 impl<T: Expression> PgExpressionMethods for T {}
 
-use super::date_and_time::{AtTimeZone, DateTimeLike};
-use crate::sql_types::VarChar;
-
 /// PostgreSQL specific methods present on timestamp expressions.
 #[cfg(feature = "postgres_backend")]
 pub trait PgTimestampExpressionMethods: Expression + Sized {
@@ -1572,6 +1570,231 @@ pub trait PgJsonbExpressionMethods: Expression + Sized {
     {
         Grouped(IsContainedByJsonb::new(self, other.as_expression()))
     }
+
+    /// Creates a PostgreSQL `-` expression.
+    ///
+    /// This operator removes the value associated with the given key, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let santas_address: serde_json::Value = serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "postcode": "99705",
+    ///     "state": "Alaska"
+    /// });
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_modified_address = contacts.select(address.remove("postcode")).get_result::<serde_json::Value>(conn)?;
+    /// assert_eq!(santas_modified_address, serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "state": "Alaska"
+    /// }));
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_modified_address = contacts.select(address.remove(vec!["postcode", "state"])).get_result::<serde_json::Value>(conn)?;
+    /// assert_eq!(santas_modified_address, serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    /// }));
+    ///
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_address_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.remove(1))
+    ///                             .get_result::<serde_json::Value>(conn)?;
+    ///
+    /// let roberts_first_address = serde_json::json!([{
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    /// }]);
+    /// assert_eq!(roberts_first_address, roberts_address_in_db);
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    ///
+    fn remove<T>(
+        self,
+        other: T,
+    ) -> dsl::RemoveFromJsonb<Self, T::Expression, <T::Expression as Expression>::SqlType>
+    where
+        T: JsonRemoveIndex,
+        <T::Expression as Expression>::SqlType: SqlType,
+    {
+        Grouped(RemoveFromJsonb::new(
+            self,
+            other.into_json_index_expression(),
+        ))
+    }
+
+    /// Creates a PostgreSQL `#-` expression.
+    ///
+    /// This operator removes the value associated with the given json path, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let santas_address: serde_json::Value = serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "postcode": "99705",
+    ///     "state": "Alaska"
+    /// });
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_modified_address = contacts.select(address.remove("postcode")).get_result::<serde_json::Value>(conn)?;
+    /// assert_eq!(santas_modified_address, serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "state": "Alaska"
+    /// }));
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_modified_address = contacts.select(address.remove_by_path(vec!["postcode"])).get_result::<serde_json::Value>(conn)?;
+    /// assert_eq!(santas_modified_address, serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "state": "Alaska"
+    /// }));
+    ///
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_address_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.remove_by_path(vec!["1", "postcode"]))
+    ///                             .get_result::<serde_json::Value>(conn)?;
+    ///
+    /// let roberts_address = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    /// assert_eq!(roberts_address, roberts_address_in_db);
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    ///
+    fn remove_by_path<T>(self, other: T) -> dsl::RemoveByPathFromJsonb<Self, T::Expression>
+    where
+        T: AsExpression<Array<Text>>,
+    {
+        Grouped(RemoveByPathFromJsonb::new(self, other.as_expression()))
+    }
 }
 
 impl<T> PgJsonbExpressionMethods for T
@@ -1581,8 +1804,384 @@ where
 {
 }
 
+/// PostgreSQL specific methods present on JSON and JSONB expressions.
+#[cfg(feature = "postgres_backend")]
+pub trait PgAnyJsonExpressionMethods: Expression + Sized {
+    /// Creates a PostgreSQL `->` expression.
+    ///
+    /// This operator extracts the value associated with the given key, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
+    /// Extracts JSON object field with the given key.
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let santas_address: serde_json::Value = serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "postcode": "99705",
+    ///     "state": "Alaska"
+    /// });
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_postcode = contacts.select(address.retrieve_as_object("postcode")).get_result::<serde_json::Value>(conn)?;
+    /// assert_eq!(santas_postcode, serde_json::json!("99705"));
+    ///
+    ///
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_second_address_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.retrieve_as_object(1))
+    ///                             .get_result::<serde_json::Value>(conn)?;
+    ///
+    /// let roberts_second_address = serde_json::json!({
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    /// });
+    /// assert_eq!(roberts_second_address, roberts_second_address_in_db);
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    /// ```
+    fn retrieve_as_object<T>(
+        self,
+        other: T,
+    ) -> dsl::RetrieveAsObjectJson<Self, T::Expression, <T::Expression as Expression>::SqlType>
+    where
+        T: JsonIndex,
+        <T::Expression as Expression>::SqlType: SqlType,
+    {
+        Grouped(RetrieveAsObjectJson::new(
+            self,
+            other.into_json_index_expression(),
+        ))
+    }
+
+    /// Creates a PostgreSQL `->>` expression.
+    ///
+    /// This operator extracts the value associated with the given key, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
+    /// Extracts JSON object field as Text with the given key.
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let santas_address: serde_json::Value = serde_json::json!({
+    ///     "street": "Article Circle Expressway 1",
+    ///     "city": "North Pole",
+    ///     "postcode": "99705",
+    ///     "state": "Alaska"
+    /// });
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Claus"), address.eq(&santas_address)))
+    ///     .execute(conn)?;
+    ///
+    /// let santas_postcode = contacts.select(address.retrieve_as_text("postcode")).get_result::<String>(conn)?;
+    /// assert_eq!(santas_postcode, "99705");
+    ///
+    ///
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_second_address_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.retrieve_as_text(1))
+    ///                             .get_result::<String>(conn)?;
+    ///
+    /// let roberts_second_address = String::from(
+    ///     "{\"city\": \"New York\", \
+    ///     \"state\": \"New York\", \
+    ///     \"street\": \"Somewhere In Ny 251\", \
+    ///     \"postcode\": \"3213212\"}"
+    ///     );
+    /// assert_eq!(roberts_second_address, roberts_second_address_in_db);
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    /// ```
+    fn retrieve_as_text<T>(
+        self,
+        other: T,
+    ) -> dsl::RetrieveAsTextJson<Self, T::Expression, <T::Expression as Expression>::SqlType>
+    where
+        T: JsonIndex,
+        <T::Expression as Expression>::SqlType: SqlType,
+    {
+        Grouped(RetrieveAsTextJson::new(
+            self,
+            other.into_json_index_expression(),
+        ))
+    }
+
+    /// Creates a PostgreSQL `#>` expression.
+    ///
+    /// This operator extracts the value associated with the given key, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
+    /// Extracts JSON object field with the given key.
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_second_street_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.retrieve_by_path_as_object(vec!["1", "street"]))
+    ///                             .get_result::<serde_json::Value>(conn)?;
+    ///
+    /// assert_eq!(roberts_second_street_in_db, serde_json::json!("Somewhere In Ny 251"));
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    /// ```
+    fn retrieve_by_path_as_object<T>(
+        self,
+        other: T,
+    ) -> dsl::RetrieveByPathAsObjectJson<Self, T::Expression>
+    where
+        T: AsExpression<Array<Text>>,
+    {
+        Grouped(RetrieveByPathAsObjectJson::new(self, other.as_expression()))
+    }
+
+    /// Creates a PostgreSQL `#>>` expression.
+    ///
+    /// This operator extracts the value associated with the given key, that is provided on the
+    /// Right Hand Side of the operator.
+    ///
+    /// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
+    /// Extracts JSON object field as Text with the given key.
+    /// # Example
+    ///
+    /// ```rust
+    /// # include!("../../doctest_setup.rs");
+    /// #
+    /// # table! {
+    /// #    contacts {
+    /// #        id -> Integer,
+    /// #        name -> VarChar,
+    /// #        address -> Jsonb,
+    /// #    }
+    /// # }
+    /// #
+    /// # fn main() {
+    /// #     run_test().unwrap();
+    /// # }
+    ///
+    /// # #[cfg(feature = "serde_json")]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     use self::contacts::dsl::*;
+    /// #     let conn = &mut establish_connection();
+    /// #     diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
+    /// #     diesel::sql_query("CREATE TABLE contacts (
+    /// #         id SERIAL PRIMARY KEY,
+    /// #         name VARCHAR NOT NULL,
+    /// #         address JSONB NOT NULL
+    /// #     )").execute(conn)
+    /// #        .unwrap();
+    /// #
+    /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
+    ///     {
+    ///         "street": "Somewhere In La 251",
+    ///         "city": "Los Angeles",
+    ///         "postcode": "12231223",
+    ///         "state": "California"
+    ///     },
+    ///     {
+    ///         "street": "Somewhere In Ny 251",
+    ///         "city": "New York",
+    ///         "postcode": "3213212",
+    ///         "state": "New York"
+    ///     }
+    /// ]);
+    ///
+    /// diesel::insert_into(contacts)
+    ///     .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
+    ///     .execute(conn)?;
+    ///
+    /// let roberts_second_street_in_db = contacts
+    ///                             .filter(name.eq("Robert Downey Jr."))
+    ///                             .select(address.retrieve_by_path_as_text(vec!["1", "street"]))
+    ///                             .get_result::<String>(conn)?;
+    ///
+    /// assert_eq!(roberts_second_street_in_db, "Somewhere In Ny 251");
+    ///
+    /// #     Ok(())
+    /// # }
+    /// # #[cfg(not(feature = "serde_json"))]
+    /// # fn run_test() -> QueryResult<()> {
+    /// #     Ok(())
+    /// # }
+    /// ```
+    fn retrieve_by_path_as_text<T>(
+        self,
+        other: T,
+    ) -> dsl::RetrieveByPathAsTextJson<Self, T::Expression>
+    where
+        T: AsExpression<Array<Text>>,
+    {
+        Grouped(RetrieveByPathAsTextJson::new(self, other.as_expression()))
+    }
+}
+
+#[doc(hidden)]
+impl<T> PgAnyJsonExpressionMethods for T
+where
+    T: Expression,
+    T::SqlType: JsonOrNullableJsonOrJsonbOrNullableJsonb,
+{
+}
+
 mod private {
-    use crate::sql_types::{Array, Cidr, Inet, Jsonb, Nullable, Range, SqlType, Text};
+    use crate::sql_types::{
+        Array, Cidr, Inet, Integer, Json, Jsonb, Nullable, Range, SqlType, Text,
+    };
+    use crate::{Expression, IntoSql};
 
     /// Marker trait used to implement `ArrayExpressionMethods` on the appropriate
     /// types. Once coherence takes associated types into account, we can remove
@@ -1634,4 +2233,132 @@ mod private {
 
     impl JsonbOrNullableJsonb for Jsonb {}
     impl JsonbOrNullableJsonb for Nullable<Jsonb> {}
+
+    /// A trait that describes valid json indices used by postgresql
+    pub trait JsonRemoveIndex {
+        /// The Expression node created by this index type
+        type Expression: Expression;
+
+        /// Convert a index value into the corresponding index expression
+        fn into_json_index_expression(self) -> Self::Expression;
+    }
+
+    impl<'a> JsonRemoveIndex for &'a str {
+        type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Text>()
+        }
+    }
+
+    impl JsonRemoveIndex for String {
+        type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Text>()
+        }
+    }
+
+    impl JsonRemoveIndex for Vec<String> {
+        type Expression = crate::dsl::AsExprOf<Self, Array<Text>>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Array<Text>>()
+        }
+    }
+
+    impl<'a> JsonRemoveIndex for Vec<&'a str> {
+        type Expression = crate::dsl::AsExprOf<Self, Array<Text>>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Array<Text>>()
+        }
+    }
+
+    impl<'a> JsonRemoveIndex for &'a [&'a str] {
+        type Expression = crate::dsl::AsExprOf<Self, Array<Text>>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Array<Text>>()
+        }
+    }
+
+    impl JsonRemoveIndex for i32 {
+        type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<crate::sql_types::Int4>()
+        }
+    }
+
+    impl<T> JsonRemoveIndex for T
+    where
+        T: Expression,
+        T::SqlType: TextArrayOrTextOrInteger,
+    {
+        type Expression = Self;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self
+        }
+    }
+
+    pub trait TextArrayOrTextOrInteger {}
+
+    impl TextArrayOrTextOrInteger for Array<Text> {}
+    impl TextArrayOrTextOrInteger for Text {}
+    impl TextArrayOrTextOrInteger for Integer {}
+
+    /// Marker trait used to implement `PgAnyJsonExpressionMethods` on the appropriate types.
+    pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
+    impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
+    impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
+    impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
+    impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}
+
+    pub trait JsonIndex {
+        type Expression: Expression;
+
+        fn into_json_index_expression(self) -> Self::Expression;
+    }
+
+    impl<'a> JsonIndex for &'a str {
+        type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Text>()
+        }
+    }
+
+    impl JsonIndex for String {
+        type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<Text>()
+        }
+    }
+
+    impl JsonIndex for i32 {
+        type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self.into_sql::<crate::sql_types::Int4>()
+        }
+    }
+
+    impl<T> JsonIndex for T
+    where
+        T: Expression,
+        T::SqlType: TextOrInteger,
+    {
+        type Expression = Self;
+
+        fn into_json_index_expression(self) -> Self::Expression {
+            self
+        }
+    }
+
+    pub trait TextOrInteger {}
+    impl TextOrInteger for Text {}
+    impl TextOrInteger for Integer {}
 }
diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs
index 273d5d01cb08..63806a91683e 100644
--- a/diesel/src/pg/expression/helper_types.rs
+++ b/diesel/src/pg/expression/helper_types.rs
@@ -105,3 +105,27 @@ pub type IsContainedByJsonb<Lhs, Rhs> =
 
 /// The return type of `lhs.index(rhs)`
 pub type ArrayIndex<Lhs, Rhs> = super::operators::ArrayIndex<Lhs, AsExprOf<Rhs, Integer>>;
+
+/// The return type of `lhs.remove(rhs)`
+pub type RemoveFromJsonb<Lhs, Rhs, ST> =
+    Grouped<super::operators::RemoveFromJsonb<Lhs, AsExprOf<Rhs, ST>>>;
+
+/// The return type of `lhs.retrieve_as_object(rhs)`
+pub type RetrieveAsObjectJson<Lhs, Rhs, ST> =
+    Grouped<super::operators::RetrieveAsObjectJson<Lhs, AsExprOf<Rhs, ST>>>;
+
+/// The return type of `lhs.retrieve_as_text(rhs)`
+pub type RetrieveAsTextJson<Lhs, Rhs, ST> =
+    Grouped<super::operators::RetrieveAsTextJson<Lhs, AsExprOf<Rhs, ST>>>;
+
+/// The return type of `lhs.retrieve_by_path_as_object(rhs)`
+pub type RetrieveByPathAsObjectJson<Lhs, Rhs> =
+    Grouped<super::operators::RetrieveByPathAsObjectJson<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
+
+/// The return type of `lhs.retrieve_by_path_as_text(rhs)`
+pub type RetrieveByPathAsTextJson<Lhs, Rhs> =
+    Grouped<super::operators::RetrieveByPathAsTextJson<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
+
+/// The return type of `lhs.remove_by_path(rhs)`
+pub type RemoveByPathFromJsonb<Lhs, Rhs> =
+    Grouped<super::operators::RemoveByPathFromJsonb<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
diff --git a/diesel/src/pg/expression/operators.rs b/diesel/src/pg/expression/operators.rs
index eca1d76a7480..f90fa67712b5 100644
--- a/diesel/src/pg/expression/operators.rs
+++ b/diesel/src/pg/expression/operators.rs
@@ -2,7 +2,9 @@ use crate::expression::expression_types::NotSelectable;
 use crate::expression::{TypedExpressionType, ValidGrouping};
 use crate::pg::Pg;
 use crate::query_builder::{QueryFragment, QueryId};
-use crate::sql_types::{Array, Bigint, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType};
+use crate::sql_types::{
+    Array, Bigint, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType, Text,
+};
 
 __diesel_infix_operator!(IsDistinctFrom, " IS DISTINCT FROM ", ConstantNullability Bool, backend: Pg);
 __diesel_infix_operator!(IsNotDistinctFrom, " IS NOT DISTINCT FROM ", ConstantNullability Bool, backend: Pg);
@@ -28,6 +30,22 @@ infix_operator!(HasAnyKeyJsonb, " ?| ", backend: Pg);
 infix_operator!(HasAllKeysJsonb, " ?& ", backend: Pg);
 infix_operator!(ContainsJsonb, " @> ", backend: Pg);
 infix_operator!(IsContainedByJsonb, " <@ ", backend: Pg);
+infix_operator!(RemoveFromJsonb, " - ", Jsonb, backend: Pg);
+__diesel_infix_operator!(
+    RetrieveAsObjectJson,
+    " -> ",
+    __diesel_internal_SameResultAsInput,
+    backend: Pg
+);
+infix_operator!(RetrieveAsTextJson, " ->> ", Text, backend: Pg);
+__diesel_infix_operator!(
+    RetrieveByPathAsObjectJson,
+    " #> ",
+    __diesel_internal_SameResultAsInput,
+    backend: Pg
+);
+infix_operator!(RetrieveByPathAsTextJson, " #>> ", Text, backend: Pg);
+infix_operator!(RemoveByPathFromJsonb, " #-", Jsonb, backend: Pg);
 
 #[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)]
 #[doc(hidden)]


Test patch:
diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
index e710b4ed73f8..394ac9527adb 100644
--- a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
+++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
@@ -110,7 +110,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<NoFromClause>` is
               <(T0, T1) as SelectableExpression<QS>>
               <(T0, T1, T2) as SelectableExpression<QS>>
               <(T0, T1, T2, T3) as SelectableExpression<QS>>
-            and 149 others
+            and 155 others
     = note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
     = note: 1 redundant requirements hidden
     = note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -134,7 +134,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
               <(T0, T1) as ValidGrouping<__GroupByClause>>
               <(T0, T1, T2) as ValidGrouping<__GroupByClause>>
               <(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
-            and 134 others
+            and 140 others
     = note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
     = note: 1 redundant requirements hidden
     = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -152,7 +152,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<NoFromClause>` is
              <(T0, T1) as SelectableExpression<QS>>
              <(T0, T1, T2) as SelectableExpression<QS>>
              <(T0, T1, T2, T3) as SelectableExpression<QS>>
-           and 149 others
+           and 155 others
    = note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
    = note: 1 redundant requirements hidden
    = note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -171,7 +171,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
              <(T0, T1) as ValidGrouping<__GroupByClause>>
              <(T0, T1, T2) as ValidGrouping<__GroupByClause>>
              <(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
-           and 134 others
+           and 140 others
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
    = note: 1 redundant requirements hidden
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -189,7 +189,7 @@ error[E0277]: the trait bound `{integer}: QueryFragment<Pg>` is not satisfied
              <() as QueryFragment<DB>>
              <(T0, T1) as QueryFragment<__DB>>
              <(T0, T1, T2) as QueryFragment<__DB>>
-           and 261 others
+           and 267 others
    = note: required because of the requirements on the impl of `QueryFragment<Pg>` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
    = note: 3 redundant requirements hidden
    = note: required because of the requirements on the impl of `QueryFragment<Pg>` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>>>`
@@ -206,7 +206,7 @@ error[E0277]: the trait bound `{integer}: QueryId` is not satisfied
              <() as QueryId>
              <(T0, T1) as QueryId>
              <(T0, T1, T2) as QueryId>
-           and 224 others
+           and 230 others
    = note: required because of the requirements on the impl of `QueryId` for `({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>)`
    = note: 3 redundant requirements hidden
    = note: required because of the requirements on the impl of `QueryId` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::internal::derives::as_expression::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>>>`
@@ -228,6 +228,6 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(T0, T1) as diesel::Expression>
              <(T0, T1, T2) as diesel::Expression>
              <(T0, T1, T2, T3) as diesel::Expression>
-           and 114 others
+           and 120 others
    = note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Double>` for `{integer}`
    = note: required because of the requirements on the impl of `AsExpressionList<diesel::sql_types::Double>` for `({integer}, f64)`
diff --git a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
index 63e2b58c2fbf..474029af0af7 100644
--- a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
+++ b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
@@ -32,7 +32,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(T0, T1) as diesel::Expression>
              <(T0, T1, T2) as diesel::Expression>
              <(T0, T1, T2, T3) as diesel::Expression>
-           and 118 others
+           and 124 others
    = note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
    = note: required because of the requirements on the impl of `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<FromClause<string_primary_key::table>>`
 
@@ -47,7 +47,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
              <(T0, T1) as ValidGrouping<__GroupByClause>>
              <(T0, T1, T2) as ValidGrouping<__GroupByClause>>
              <(T0, T1, T2, T3) as ValidGrouping<__GroupByClause>>
-           and 140 others
+           and 146 others
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
    = note: required because of the requirements on the impl of `NonAggregate` for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>`
    = note: required because of the requirements on the impl of `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<FromClause<string_primary_key::table>>`
diff --git a/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr b/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
index ebf354e121a3..e5677bc9515c 100644
--- a/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
+++ b/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
@@ -9,5 +9,5 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(T0, T1) as diesel::Expression>
              <(T0, T1, T2) as diesel::Expression>
              <(T0, T1, T2, T3) as diesel::Expression>
-           and 118 others
+           and 124 others
    = note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Text>` for `{integer}`