Skip to content

Commit

Permalink
Merge pull request #4253 from valkrypton/feat/add/json_object_with_k_v
Browse files Browse the repository at this point in the history
implement json_object with separate keys and values arguments
  • Loading branch information
weiznich authored Sep 13, 2024
2 parents ab8903e + b488df4 commit bfd2e45
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 2 deletions.
19 changes: 17 additions & 2 deletions diesel/src/pg/expression/expression_methods.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! PostgreSQL specific expression methods
pub(in crate::pg) use self::private::{
ArrayOrNullableArray, InetOrCidr, JsonIndex, JsonOrNullableJson,
ArrayOrNullableArray, CombinedNullableValue, InetOrCidr, JsonIndex, JsonOrNullableJson,
JsonOrNullableJsonOrJsonbOrNullableJsonb, JsonRemoveIndex, JsonbOrNullableJsonb,
MaybeNullableValue, MultirangeOrNullableMultirange, MultirangeOrRangeMaybeNullable,
RangeHelper, RangeOrNullableRange, TextArrayOrNullableTextArray, TextOrNullableText,
Expand Down Expand Up @@ -3419,7 +3419,7 @@ where
pub(in crate::pg) mod private {
use crate::sql_types::{
Array, Binary, Cidr, Inet, Integer, Json, Jsonb, MaybeNullableType, Multirange, Nullable,
Range, SingleValue, SqlType, Text,
OneIsNullable, Range, SingleValue, SqlType, Text,
};
use crate::{Expression, IntoSql};

Expand Down Expand Up @@ -3723,6 +3723,21 @@ pub(in crate::pg) mod private {
type Out = <T::IsNull as MaybeNullableType<O>>::Out;
}

pub trait CombinedNullableValue<O, Out>: SingleValue {
type Out: SingleValue;
}

impl<T, O, Out> CombinedNullableValue<O, Out> for T
where
T: SingleValue,
O: SingleValue,
T::IsNull: OneIsNullable<O::IsNull>,
<T::IsNull as OneIsNullable<O::IsNull>>::Out: MaybeNullableType<Out>,
<<T::IsNull as OneIsNullable<O::IsNull>>::Out as MaybeNullableType<Out>>::Out: SingleValue,
{
type Out = <<T::IsNull as OneIsNullable<O::IsNull>>::Out as MaybeNullableType<Out>>::Out;
}

#[diagnostic::on_unimplemented(
message = "`{Self}` is neither `Array<Text>`, `Array<Nullable<Text>>`,\
`Nullable<Array<Text>>` nor `diesel::sql_types::Nullable<Array<Nullable<Text>>>`",
Expand Down
51 changes: 51 additions & 0 deletions diesel/src/pg/expression/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use super::expression_methods::InetOrCidr;
use crate::expression::functions::define_sql_function;
use crate::pg::expression::expression_methods::ArrayOrNullableArray;
use crate::pg::expression::expression_methods::CombinedNullableValue;
use crate::pg::expression::expression_methods::JsonOrNullableJson;
use crate::pg::expression::expression_methods::JsonbOrNullableJsonb;
use crate::pg::expression::expression_methods::MaybeNullableValue;
Expand Down Expand Up @@ -1609,6 +1610,7 @@ define_sql_function! {
fn to_jsonb<E: MaybeNullableValue<Jsonb>>(e: E) -> E::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// Builds a JSON object out of a text array. The array must have an even number of members,
/// in which case they are taken as alternating key/value pairs
Expand Down Expand Up @@ -1654,6 +1656,55 @@ define_sql_function! {
) -> 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,
Arr2: TextArrayOrNullableTextArray + CombinedNullableValue<Arr1, Json>,
>(
keys: Arr1,
values: Arr2,
) -> Arr2::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// Returns the type of the top-level json value as a text-string
Expand Down
6 changes: 6 additions & 0 deletions diesel/src/pg/expression/helper_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,12 @@ pub type to_jsonb<E> = super::functions::to_jsonb<SqlTypeOf<E>, E>;
#[cfg(feature = "postgres_backend")]
pub type json_object<A> = super::functions::json_object<SqlTypeOf<A>, A>;

/// Return type of [`json_object_with_keys_and_values(text_array, text_array)`](super::functions::json_object_with_keys_and_values())
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
pub type json_object_with_keys_and_values<K, V> =
super::functions::json_object_with_keys_and_values<SqlTypeOf<K>, SqlTypeOf<V>, K, V>;

/// Return type of [`json_typeof(json)`](super::functions::json_typeof())
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
Expand Down
1 change: 1 addition & 0 deletions diesel_derives/tests/auto_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ fn postgres_functions() -> _ {
to_json(pg_extras::id),
to_jsonb(pg_extras::id),
json_object(pg_extras::text_array),
json_object_with_keys_and_values(pg_extras::text_array, pg_extras::text_array),
json_typeof(pg_extras::json),
jsonb_typeof(pg_extras::jsonb),
jsonb_pretty(pg_extras::jsonb),
Expand Down

0 comments on commit bfd2e45

Please sign in to comment.