use super::{SetClause, batch_update::*};
use crate::associations::HasTable;
use crate::backend::DieselReserveSpecialization;
use crate::expression::AppearsOnTable;
use crate::expression::grouped::Grouped;
use crate::expression::operators::Eq;
use crate::query_builder::*;
use crate::query_source::{Column, QuerySource};
use crate::{Identifiable, Table};
use alloc::borrow::ToOwned;
pub trait AsChangeset {
type Target: QuerySource;
type Changeset;
#[doc(hidden)]
const SET_CLAUSE: SetClause = SetClause::Immediate;
#[allow(clippy::wrong_self_convention)]
fn as_changeset(self) -> Self::Changeset;
}
#[allow(unreachable_pub)]
#[doc(inline)]
pub use diesel_derives::AsChangeset;
impl<T: AsChangeset> AsChangeset for Option<T> {
type Target = T::Target;
type Changeset = Option<T::Changeset>;
fn as_changeset(self) -> Self::Changeset {
self.map(AsChangeset::as_changeset)
}
}
impl<'update, T> AsChangeset for &'update Option<T>
where
&'update T: AsChangeset,
{
type Target = <&'update T as AsChangeset>::Target;
type Changeset = Option<<&'update T as AsChangeset>::Changeset>;
fn as_changeset(self) -> Self::Changeset {
self.as_ref().map(AsChangeset::as_changeset)
}
}
impl<Left, Right> AsChangeset for Eq<Left, Right>
where
Left: AssignmentTarget,
Right: AppearsOnTable<Left::Table>,
{
type Target = Left::Table;
type Changeset = Assign<<Left as AssignmentTarget>::QueryAstNode, Right>;
fn as_changeset(self) -> Self::Changeset {
Assign {
target: self.left.into_target(),
expr: self.right,
}
}
}
impl<Left, Right> AsChangeset for Grouped<Eq<Left, Right>>
where
Eq<Left, Right>: AsChangeset,
{
type Target = <Eq<Left, Right> as AsChangeset>::Target;
type Changeset = <Eq<Left, Right> as AsChangeset>::Changeset;
fn as_changeset(self) -> Self::Changeset {
self.0.as_changeset()
}
}
#[derive(Debug, Clone, Copy, QueryId)]
pub struct Assign<Target, Expr> {
pub(crate) target: Target,
expr: Expr,
}
impl<T, U, DB> QueryFragment<DB> for Assign<T, U>
where
DB: Backend,
T: QueryFragment<DB>,
U: QueryFragment<DB>,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
QueryFragment::walk_ast(&self.target, out.reborrow())?;
out.push_sql(" = ");
QueryFragment::walk_ast(&self.expr, out.reborrow())
}
}
pub trait AssignmentTarget {
type Table: Table;
type QueryAstNode;
fn into_target(self) -> Self::QueryAstNode;
}
#[derive(Debug, Clone, Copy)]
pub struct ColumnWrapperForUpdate<C>(pub C);
impl<DB, C> QueryFragment<DB> for ColumnWrapperForUpdate<C>
where
DB: Backend + DieselReserveSpecialization,
C: Column,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
out.push_identifier(C::NAME)
}
}
impl<C> AssignmentTarget for C
where
C: Column,
{
type Table = C::Table;
type QueryAstNode = ColumnWrapperForUpdate<C>;
fn into_target(self) -> Self::QueryAstNode {
ColumnWrapperForUpdate(self)
}
}
#[cfg(any(
feature = "__sqlite-shared",
feature = "postgres_backend",
feature = "mysql_backend",
feature = "mariadb_backend"
))]
impl<C, T, DB> BatchValueHelper<DB> for Assign<ColumnWrapperForUpdate<C>, T>
where
DB: Backend + DieselReserveSpecialization,
C: Column + QueryFragment<DB>,
T: QueryFragment<DB>,
Self: BatchAssignHelper<DB>,
{
fn assign<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
self.batch_assign_identifier(out.reborrow())?;
out.push_sql(" = ");
out.push_identifier(BATCH_UPDATE_ALIAS)?;
out.push_sql(".");
out.push_identifier(C::NAME)?;
Ok(())
}
fn column_name<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
self.target.walk_ast(out)
}
fn bind_value<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
self.expr.walk_ast(out)
}
}
impl<'a, U, I, C, PK> AsChangeset for &'a [U]
where
U: AsChangeset + HasTable<Table = U::Target>,
U::Target: Table<PrimaryKey = PK>,
&'a U: AsChangeset<Target = U::Target, Changeset = C> + Identifiable<Table = U::Target, Id = I>,
{
type Target = U::Target;
type Changeset = BatchUpdate<I, C, PK, U::Target>;
const SET_CLAUSE: SetClause = SetClause::Delegated;
fn as_changeset(self) -> Self::Changeset {
let values = self
.iter()
.map(|value| (Identifiable::id(value), AsChangeset::as_changeset(value)))
.collect::<Vec<_>>();
BatchUpdate::new(values, U::table().primary_key())
}
}
impl<'a, U> AsChangeset for &'a Vec<U>
where
U: AsChangeset,
&'a [U]: AsChangeset,
{
type Target = U::Target;
type Changeset = <&'a [U] as AsChangeset>::Changeset;
const SET_CLAUSE: SetClause = <&'a [U] as AsChangeset>::SET_CLAUSE;
fn as_changeset(self) -> Self::Changeset {
(&**self).as_changeset()
}
}
impl<'a, U, const N: usize> AsChangeset for &'a [U; N]
where
&'a [U]: AsChangeset,
{
type Target = <&'a [U] as AsChangeset>::Target;
type Changeset = <&'a [U] as AsChangeset>::Changeset;
const SET_CLAUSE: SetClause = <&'a [U] as AsChangeset>::SET_CLAUSE;
fn as_changeset(self) -> Self::Changeset {
self.as_slice().as_changeset()
}
}
impl<U> AsChangeset for Vec<U>
where
Box<[U]>: AsChangeset,
{
type Target = <Box<[U]> as AsChangeset>::Target;
type Changeset = <Box<[U]> as AsChangeset>::Changeset;
const SET_CLAUSE: SetClause = <Box<[U]> as AsChangeset>::SET_CLAUSE;
fn as_changeset(self) -> Self::Changeset {
self.into_boxed_slice().as_changeset()
}
}
impl<U, const N: usize> AsChangeset for Box<[U; N]>
where
Box<[U]>: AsChangeset,
{
type Target = <Box<[U]> as AsChangeset>::Target;
type Changeset = <Box<[U]> as AsChangeset>::Changeset;
const SET_CLAUSE: SetClause = <Box<[U]> as AsChangeset>::SET_CLAUSE;
fn as_changeset(self) -> Self::Changeset {
(self as Box<[U]>).as_changeset()
}
}
fn batch_update<U, I, C, PK>(
values: impl IntoIterator<Item = U>,
) -> BatchUpdate<I, C, PK, U::Target>
where
U: AsChangeset<Changeset = C> + HasTable<Table = U::Target>,
U::Target: Table<PrimaryKey = PK>,
for<'a> &'a U: Identifiable<Id: IntoOwned<Owned = I>>,
{
let values = values
.into_iter()
.map(|v| {
let id = v.id().into_owned();
let changes = v.as_changeset();
(id, changes)
})
.collect();
BatchUpdate::new(values, U::table().primary_key())
}
impl<U, I, C, PK> AsChangeset for Box<[U]>
where
U: AsChangeset<Changeset = C> + HasTable<Table = U::Target>,
U::Target: Table<PrimaryKey = PK>,
for<'a> &'a U: Identifiable<Id: IntoOwned<Owned = I>>,
{
type Target = U::Target;
type Changeset = BatchUpdate<I, C, PK, U::Target>;
const SET_CLAUSE: SetClause = SetClause::Delegated;
fn as_changeset(self) -> Self::Changeset {
batch_update(self)
}
}
impl<U, I, C, PK, const N: usize> AsChangeset for [U; N]
where
U: AsChangeset<Changeset = C> + HasTable<Table = U::Target>,
U::Target: Table<PrimaryKey = PK>,
for<'a> &'a U: Identifiable<Id: IntoOwned<Owned = I>>,
{
type Target = U::Target;
type Changeset = BatchUpdate<I, C, PK, U::Target>;
const SET_CLAUSE: SetClause = SetClause::Delegated;
fn as_changeset(self) -> Self::Changeset {
batch_update(self)
}
}
pub(crate) trait IntoOwned {
type Owned;
fn into_owned(self) -> Self::Owned;
}
impl<T> IntoOwned for &T
where
T: ToOwned,
{
type Owned = T::Owned;
fn into_owned(self) -> Self::Owned {
(*self).to_owned()
}
}