Skip to content

Commit

Permalink
Merge pull request #4325 from prkbuilds/pg_json_populate_record
Browse files Browse the repository at this point in the history
Adding Support for `json_populate_record` and `jsonb_populate_record`
  • Loading branch information
weiznich authored Nov 17, 2024
2 parents e659b01 + bbf1ae2 commit 27421b9
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 7 deletions.
29 changes: 22 additions & 7 deletions diesel/src/pg/expression/expression_methods.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! PostgreSQL specific expression methods

pub(in crate::pg) use self::private::{
ArrayOrNullableArray, CombinedNullableValue, InetOrCidr, JsonIndex, JsonOrNullableJson,
JsonOrNullableJsonOrJsonbOrNullableJsonb, JsonRemoveIndex, JsonbOrNullableJsonb,
MaybeNullableValue, MultirangeOrNullableMultirange, MultirangeOrRangeMaybeNullable,
RangeOrMultirange, RangeOrNullableRange, RecordOrNullableRecord, TextArrayOrNullableTextArray,
TextOrNullableText,
ArrayOrNullableArray, CombinedAllNullableValue, CombinedNullableValue, InetOrCidr, JsonIndex,
JsonOrNullableJson, JsonOrNullableJsonOrJsonbOrNullableJsonb, JsonRemoveIndex,
JsonbOrNullableJsonb, MaybeNullableValue, MultirangeOrNullableMultirange,
MultirangeOrRangeMaybeNullable, RangeOrMultirange, RangeOrNullableRange,
RecordOrNullableRecord, TextArrayOrNullableTextArray, TextOrNullableText,
};
use super::date_and_time::{AtTimeZone, DateTimeLike};
use super::operators::*;
Expand Down Expand Up @@ -3378,8 +3378,8 @@ where

pub(in crate::pg) mod private {
use crate::sql_types::{
Array, Binary, Cidr, Inet, Integer, Json, Jsonb, MaybeNullableType, Multirange, Nullable,
OneIsNullable, Range, Record, SingleValue, SqlType, Text,
AllAreNullable, Array, Binary, Cidr, Inet, Integer, Json, Jsonb, MaybeNullableType,
Multirange, Nullable, OneIsNullable, Range, Record, SingleValue, SqlType, Text,
};
use crate::{Expression, IntoSql};

Expand Down Expand Up @@ -3726,4 +3726,19 @@ pub(in crate::pg) mod private {

impl<T> RecordOrNullableRecord for Record<T> {}
impl<T> RecordOrNullableRecord for Nullable<Record<T>> {}

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

impl<T, O, Out> CombinedAllNullableValue<O, Out> for T
where
T: SingleValue,
O: SingleValue,
T::IsNull: AllAreNullable<O::IsNull>,
<T::IsNull as AllAreNullable<O::IsNull>>::Out: MaybeNullableType<Out>,
<<T::IsNull as AllAreNullable<O::IsNull>>::Out as MaybeNullableType<Out>>::Out: SingleValue,
{
type Out = <<T::IsNull as AllAreNullable<O::IsNull>>::Out as MaybeNullableType<Out>>::Out;
}
}
119 changes: 119 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::CombinedAllNullableValue;
use crate::pg::expression::expression_methods::CombinedNullableValue;
use crate::pg::expression::expression_methods::JsonOrNullableJson;
use crate::pg::expression::expression_methods::JsonbOrNullableJsonb;
Expand Down Expand Up @@ -2279,3 +2280,121 @@ define_sql_function! {
#[sql_name = "row_to_json"]
fn row_to_json<R: RecordOrNullableRecord + MaybeNullableValue<Json>>(record: R) -> R::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// This function `json_populate_record` takes a Record base and Json as an input and converts it to top-level
/// JSON object to a row having the composite type of the base argument.
///
/// # 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::json_populate_record;
/// # use diesel::dsl::sql;
/// # use diesel::sql_types::{Record, Text, Integer, Json};
/// # use serde_json::Value;
/// # let connection = &mut establish_connection();
///
/// let expected: Value = serde_json::json!({
/// "f1": "Alice",
/// "f2": 16
/// });
/// let record: (String, i32) = diesel::select(json_populate_record::<Record<(Text, Integer)>, Json, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("Alice".to_string(), 16));
///
/// let expected: Value = serde_json::json!({});
/// let record: (String, i32) = diesel::select(json_populate_record::<Record<(Text, Integer)>, Json, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("John".to_string(), 30));
///
/// let expected: Value = serde_json::json!({
/// "f1": "Alice"
/// });
/// let record: (String, i32) = diesel::select(json_populate_record::<Record<(Text, Integer)>, Json, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("Alice".to_string(), 30));
///
/// # Ok(())
/// # }
/// ```
#[sql_name = "json_populate_record"]
fn json_populate_record<
B: RecordOrNullableRecord + SingleValue,
J: JsonOrNullableJson + CombinedAllNullableValue<Json, B>
>(base: B, from_json: J) -> J::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// This function `jsonb_populate_record` takes a Record base and Jsonb as an input and converts it to top-level
/// JSON object to a row having the composite type of the base argument.
///
/// # 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_populate_record;
/// # use diesel::dsl::sql;
/// # use diesel::sql_types::{Record, Text, Integer, Jsonb};
/// # use serde_json::Value;
/// # let connection = &mut establish_connection();
///
/// let expected: Value = serde_json::json!({
/// "f1": "Alice",
/// "f2": 16
/// });
/// let record: (String, i32) = diesel::select(jsonb_populate_record::<Record<(Text, Integer)>, Jsonb, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("Alice".to_string(), 16));
///
/// let expected: Value = serde_json::json!({});
/// let record: (String, i32) = diesel::select(jsonb_populate_record::<Record<(Text, Integer)>, Jsonb, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("John".to_string(), 30));
///
/// let expected: Value = serde_json::json!({
/// "f2": 42,
/// });
/// let record: (String, i32) = diesel::select(jsonb_populate_record::<Record<(Text, Integer)>, Jsonb, _, _>(
/// sql::<Record<(Text, Integer)>>("ROW('John', 30)"),
/// expected
/// )).get_result(connection)?;
/// assert_eq!(record, ("John".to_string(), 42));
///
/// # Ok(())
/// # }
/// ```
#[sql_name = "jsonb_populate_record"]
fn jsonb_populate_record<
B: RecordOrNullableRecord + SingleValue,
J: JsonbOrNullableJsonb + CombinedAllNullableValue<Jsonb, B>
>(base: B, from_json: J) -> J::Out;
}
12 changes: 12 additions & 0 deletions diesel/src/pg/expression/helper_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,3 +556,15 @@ pub type jsonb_object_with_keys_and_values<K, V> =
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
pub type row_to_json<R> = super::functions::row_to_json<SqlTypeOf<R>, R>;

/// Return type of [`json_populate_record(base, json)`](super::functions::json_populate_record())
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
pub type json_populate_record<B, J> =
super::functions::json_populate_record<SqlTypeOf<B>, SqlTypeOf<J>, B, J>;

/// Return type of [`jsonb_populate_record(base, json)`](super::functions::jsonb_populate_record())
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
pub type jsonb_populate_record<B, J> =
super::functions::jsonb_populate_record<SqlTypeOf<B>, SqlTypeOf<J>, B, J>;
2 changes: 2 additions & 0 deletions diesel_derives/tests/auto_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,8 @@ fn postgres_functions() -> _ {
jsonb_object(pg_extras::text_array),
jsonb_object_with_keys_and_values(pg_extras::text_array, pg_extras::text_array),
row_to_json(pg_extras::record),
json_populate_record(pg_extras::record, pg_extras::json),
jsonb_populate_record(pg_extras::record, pg_extras::jsonb),
)
}

Expand Down

0 comments on commit 27421b9

Please sign in to comment.