Repository: diesel-rs/diesel
File: diesel/src/pg/expression/functions.rs (Rust)

Code before:
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # #[cfg(feature = "serde_json")]
    /// # fn run_test() -> QueryResult<()> {
    /// #     use diesel::dsl::json_array_length;
    /// #     use serde_json::json;
    /// #     use diesel::sql_types::{Integer, Json};
    /// #     let connection = &mut establish_connection();
    /// let result = diesel::select(json_array_length::<Json, _>(json!([1, 2, 3])))
    ///     .get_result::<i32>(connection)?;
    ///
    /// assert_eq!(result, 3);
    ///
    /// let result = diesel::select(json_array_length::<Json, _>(json!([])))
    ///     .get_result::<i32>(connection)?;
    ///
    /// assert_eq!(result, 0);
    ///
    ///
    ///
    /// #     Ok(())
    /// # }
    /// ```

    fn json_array_length<E: JsonOrNullableJson + SingleValue>(e: E) -> Integer;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
    /// Returns the number of elements in the top-level JSON array
    ///
    ///
    /// # Example
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # fn main() {
    /// #     #[cfg(feature = "serde_json")]
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # #[cfg(feature = "serde_json")]
    /// # fn run_test() -> QueryResult<()> {
    /// #     use diesel::dsl::jsonb_array_length;
    /// #     use serde_json::json;
    /// #     use diesel::sql_types::{Integer, Jsonb};
    /// #     let connection = &mut establish_connection();
    /// let result = diesel::select(jsonb_array_length::<Jsonb, _>(json!([1, 2, 3])))
    ///     .get_result::<i32>(connection)?;

Review comment (suggestion):
Sorry for taking a few days to write this answer, this somehow got lost between all the other PR's :disappointed: 

This is already mostly the correct signature. We cannot restrict this function to only accept json arrays at compile time as that's a purely runtime concept, so it's fine to accept `JsonOrNullableJson` here. What needs to be done is to make the nullability of the return value dependend on the input nullability, which means if you put in a `Nullable<Json>` we want to return a `Nullable<Integer>` as the function returns `null` for `null` inputs.

You can do that by changing the function signature as follows:

```suggestion
    fn json_array_length<E: JsonOrNullableJson + MaybeNullableValue<Integer>>(e: E) -> E::Out;
```

As for the test: You should be able to test for null values like this:

```
let result = diesel::select(json_array_length::<Nullable<Json>, _>(None::<serde_json::Value>)).get_result::<Option<i32>(conn);
```

(The same applies to the jsonb variant of this function)
```

Code after:
    ///
    /// #     Ok(())
    /// # }
    /// ```
    fn json_object<Arr: TextArrayOrNullableTextArray + MaybeNullableValue<Json>>(
        text_array: Arr,
    ) -> Arr::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
    /// This form of json_object takes keys and values pairwise from two separate arrays.
    /// In all other respects it is identical to the one-argument form.
    ///
    /// # Example
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use diesel::dsl::json_object_with_keys_and_values;
    /// #     use diesel::sql_types::{Array, Json, Nullable, Text};
    /// #     use serde_json::Value;
    /// #     let connection = &mut establish_connection();
    /// let json = diesel::select(json_object_with_keys_and_values::<Array<Text>, Array<Text>, _, _>(
    ///             vec!["hello","John"],vec!["world","Doe"]))
    ///             .get_result::<Value>(connection)?;
    /// let expected:Value = serde_json::json!({"hello":"world","John":"Doe"});
    /// assert_eq!(expected,json);
    ///
    /// let json = diesel::select(json_object_with_keys_and_values::<Nullable<Array<Text>>, Nullable<Array<Text>>, _, _>(
    ///             Some(vec!["hello","John"]), None::<Vec<String>>))
    ///             .get_result::<Option<Value>>(connection)?;
    /// assert_eq!(None::<Value>,json);
    ///
    /// let empty: Vec<String> = Vec::new();
    /// let json = diesel::select(json_object_with_keys_and_values::<Array<Text>, Array<Text>, _, _>(
    ///             vec!["hello","John"], empty))
    ///             .get_result::<Value>(connection);
    /// assert!(json.is_err());
    ///
    /// #     Ok(())
    /// # }
    /// ```
    #[sql_name = "json_object"]
    fn json_object_with_keys_and_values<
        Arr1: TextArrayOrNullableTextArray + SingleValue,