Skip to content

Commit

Permalink
Merge pull request #4434 from IamTossan/feat/add-jsonb-insert-fn
Browse files Browse the repository at this point in the history
feat(pg): add jsonb_insert fn
  • Loading branch information
weiznich authored Jan 17, 2025
2 parents 25cff1d + 9cb4a0f commit fa871e7
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 1 deletion.
129 changes: 129 additions & 0 deletions diesel/src/pg/expression/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2666,3 +2666,132 @@ define_sql_function! {
Arr: TextArrayOrNullableTextArray + CombinedNullableValue<E,Jsonb>,
>(base: E, path: Arr, new_value: E, create_if_missing: Bool, null_value_treatment: NullValueTreatmentEnum) -> Arr::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// Returns target with `new_value` inserted into `base`.
///
/// If the item designated by the `path` is an array element, `new_value` will be inserted before that item
///
/// If the item designated by the `path` is an object field, `new_value` will be
/// inserted only if the object does not already contain that key.
///
/// * All earlier steps in the path must exist, or the target is returned unchanged.
/// * As with the path oriented operators, negative integers that appear in the `path` count
/// from the end of JSON arrays.
/// * If the last `path` step is an array index that is out of range,
/// the new value is added at the beginning of the array if the index is negative,
/// or at the end of the array if it is positive.
///
/// # 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_insert;
/// # use diesel::sql_types::{Jsonb, Array, Json, Nullable, Text};
/// # use serde_json::{json,Value};
/// # let connection = &mut establish_connection();
///
/// let result = diesel::select(jsonb_insert::<Jsonb, Array<Text>, _, _, _>(
/// json!({"a":[0,1,2]}),
/// vec!["a","1"],
/// json!("new_value"),
/// )).get_result::<Value>(connection)?;
/// let expected: Value = json!({"a":[0,"new_value",1,2]});
/// assert_eq!(result, expected);
///
/// let result = diesel::select(jsonb_insert::<Nullable<Jsonb>, Array<Text>, _, _, _>(
/// None::<serde_json::Value>,
/// vec!["a","1"],
/// Some(json!("new_value")),
/// )).get_result::<Option<Value>>(connection)?;
/// assert_eq!(result, None);
///
/// # Ok(())
/// # }
/// ```
fn jsonb_insert<
E: JsonbOrNullableJsonb + SingleValue,
Arr: TextArrayOrNullableTextArray + CombinedNullableValue<E, Jsonb>,
>(
base: E,
path: Arr,
new_value: E,
) -> Arr::Out;
}

#[cfg(feature = "postgres_backend")]
define_sql_function! {
/// Returns target with `new_value` inserted into `base`.
///
/// If the item designated by the `path` is an array element, `new_value` will be inserted before that
/// item if `insert_after` is false (which is the default),
/// or after it if `insert_after` is true.
///
/// If the item designated by the `path` is an object field, `new_value` will be inserted only
/// if the object does not already contain that key.
///
/// * All earlier steps in the `path` must exist, or the target is returned unchanged.
/// * As with the path oriented operators, negative integers that appear in the `path` count
/// from the end of JSON arrays.
/// * If the last `path` step is an array index that is out of range,
/// the new value is added at the beginning of the array if the index is negative,
/// or at the end of the array if it is positive.
///
/// # 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_insert_with_insert_after;
/// # use diesel::sql_types::{Jsonb, Array, Json, Nullable, Text};
/// # use serde_json::{json,Value};
/// # let connection = &mut establish_connection();
///
/// let result = diesel::select(jsonb_insert_with_insert_after::<Jsonb, Array<Text>, _, _, _, _>(
/// json!({"a":[0,1,2]}),
/// vec!["a","1"],
/// json!("new_value"),
/// false
/// )).get_result::<Value>(connection)?;
/// let expected: Value = json!({"a":[0,"new_value",1,2]});
/// assert_eq!(result, expected);
///
/// let result = diesel::select(jsonb_insert_with_insert_after::<Jsonb, Array<Text>, _, _, _, _>(
/// json!({"a":[0,1,2]}),
/// vec!["a","1"],
/// json!("new_value"),
/// true
/// )).get_result::<Value>(connection)?;
/// let expected: Value = json!({"a":[0,1,"new_value",2,]});
/// assert_eq!(result, expected);
///
/// # Ok(())
/// # }
/// ```
#[sql_name = "jsonb_insert"]
fn jsonb_insert_with_insert_after<
E: JsonbOrNullableJsonb + SingleValue,
Arr: TextArrayOrNullableTextArray + CombinedNullableValue<E, Jsonb>,
>(
base: E,
path: Arr,
new_value: E,
insert_after: Bool,
) -> Arr::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 @@ -609,3 +609,15 @@ pub type jsonb_set_create_if_missing<B, J, R, C> =
#[cfg(feature = "postgres_backend")]
pub type jsonb_set_lax<B, J, R, C, E> =
super::functions::jsonb_set_lax<SqlTypeOf<B>, SqlTypeOf<J>, B, J, R, C, E>;

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

/// Return type of [`jsonb_insert_with_insert_after(base, path, new_value, insert_after)`](super::functions::jsonb_insert_with_insert_after())
#[allow(non_camel_case_types)]
#[cfg(feature = "postgres_backend")]
pub type jsonb_insert_with_insert_after<B, J, R, I> =
super::functions::jsonb_insert_with_insert_after<SqlTypeOf<B>, SqlTypeOf<J>, B, J, R, I>;
3 changes: 2 additions & 1 deletion diesel/src/pg/types/uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mod tests {
];
let input_uuid = uuid::Uuid::from_slice(&bytes).unwrap();
let output_uuid =
FromSql::<Uuid, Pg>::from_sql(PgValue::for_test(input_uuid.as_bytes())).unwrap();
<uuid::Uuid as FromSql<Uuid, Pg>>::from_sql(PgValue::for_test(input_uuid.as_bytes()))
.unwrap();
assert_eq!(input_uuid, output_uuid);
}

Expand Down
7 changes: 7 additions & 0 deletions diesel_derives/tests/auto_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,13 @@ fn postgres_functions() -> _ {
pg_extras::boolean,
null_value_treatment,
),
jsonb_insert(pg_extras::jsonb, pg_extras::text_array, pg_extras::jsonb),
jsonb_insert_with_insert_after(
pg_extras::jsonb,
pg_extras::text_array,
pg_extras::jsonb,
pg_extras::boolean,
),
)
}

Expand Down

0 comments on commit fa871e7

Please sign in to comment.